I'm writing a testbench for a SPI interface. The interface is basically composed by four signals :
spi_clk : Signal clock provvided by the master
spi_cs : Chip select signal driven by the master
spi_miso : Input signal of the Master ( output signal of the slave)
spi_mosi : Output signal of the Master ( input signal of the slave)
I've traced the SPI Bus with an analyzer and I get file that shows every operation performed over the bus. Each operation begin with the falling edge of the chip select and it end with the rising edge os the chip select. The file is :
..................
03fff57000000000
03fff57400000000
03fff57800000000
03fff57c00000000
02f0fffec0a3
02f0fffcfc0c
03fff54000000000
03fff54400000000
03fff54800000000
03fff54c00000000
03fff57c00000000
03f0fffc0000
03f0fffe0000
03fff55000000000
03fff55400000000
03fff55800000000
..... and so on
Each line represents a spi operation on the bus. My problem to write the testbench is about the length of the SPI operation. It's simple to see that the byte transfered in one operation is variable. My will was to use the functions readline and hread to get the value line by line and feed my module, for example :
process
file miso_file : TEXT is in "TestBench/MISO_DATA.txt";
variable miso_line : LINE;
variable VAR_miso : std_logic_vector(63 downto 0) := (others => '0');
file mosi_file : TEXT is in "TestBench/MOSI_DATA.txt";
variable mosi_line : LINE;
variable VAR_mosi : std_logic_vector(63 downto 0) := (others => '0');
variable mosi_good : boolean;
variable miso_good : boolean;
begin
... some code ...
while not(endfile(miso_file)) loop
readline(miso_file,miso_line);
hread(miso_line,VAR_miso,miso_good);
...some code...
end loop;
wait;
end process;
This code works when the line is composed by 16 chars ( 64 bits ) but when the size is different it doesn't work. Does anyone have suggestions ?
Thanks a lot
Once hread() has consumed the line you don't know how many bytes were originally in it. You need to save miso_line'length before calling hread() and then have your testbench use that to determine what parts of the vector to send.
Related
Lets say I have an entity that looks like this:
constant NULL_PORT : std_logic_vector(1 to 0);
entity some_verification_ip is
port (
write_port : in std_logic_vector := NULL_PORT;
read_port : out std_logic_vector -- Sized on connection
);
In one test, a user's DUT only does reads, and so only connects the read port, and the write port gets sized from the default assignment to the NULL_PORT constant.
In another test, a user's DUT only does writes, and only connects the write port, but you get an elaboration error because the read_port is not sized. So the user then needs a dummy_signal in the testbench (that isnt used) just to avoid this error.
You cannot do a := assignment to the output, as this is an initial value, and you get an elaboration error when the port is connected as the NULL_PORT width missmatches the user's port connection.
Is the dummy signal in the testbench the only way to get a size? Is this changing in VHDL 2018?
NOTE: In reality, my Verification IP is an AXI4 memory model, and in the design different parts handle reads and writes, so the various channels are not needed for all tests, but are needed for larger scale tests that cover reads and writes.
As #paebbles writes in the comments you can do such a thing when you add one or two generics to the module.
entity some_verification_ip is
generic (
in_width : positive; -- could use a := <default width>
out_width : positive -- could use a := <default width>
);
port (
write_port : in std_logic_vector(in_width-1 downto 0);
read_port : out std_logic_vector(out_width-1 downto 0)
);
And when i stantiating you can use
.
.
generic map(
in_witdth => inputsignal'length,
out_width => outputsignal'length
)
port map (
.
.
If the in- and output widths are the same one generic can be used.
Initialising can then be done in a function.
I think this should work, but maybe i forgot something. The example should be enough to get yoh started though.
function null_port(width : positive) return std_logic_vector is
ret : std_logic_vector(width-1 downto 0);
begin
ret := (others => '0'); --use loops or other more elaborate stuff to construct the value you need
return ret;
end function
In testbench, I have an issue with a procedure that I want to monitor its input parameter which is a signal, this signal may contains a number of my internal inputs AND/OR outputs of a module/s or top level design entity. Now the problem is how can I read continually this signal which may dynamically changes.
TYPE data_record IS ARRAY (natural range <>) OF STD_LOGIC;
TYPE data_name_record IS ARRAY(natural range <>) OF STRING(1 TO 32);
PROCEDURE MONITORING_VALUE(
SIGNAL INPUTS_OUTPUT : IN data_record ;
EXPECTED_VALUE : IN data_record ;
INPUTS_OUTPUT_NAME : IN data_name_record;
MONITORING_TIME : IN TIME );
An alternative that I am using now is mapping this inputs/outputs to a signal of data_record_type :
SIGNAL INPUTS_ENTRED :data_record(0 TO N-1) := ('0', '0', '0', '0');
===========================================================================
-- MAPPING: --===========================================================================
INPUTS_ENTRED(0) <= input1;
INPUTS_ENTRED(1) <= input2 ;
INPUTS_ENTRED(2) <= input3;
INPUTS_ENTRED(3) <= output1;
But which this solution, I am limited with number of inputs/outputs mapped which make my procedure not useful for all, (e.g. if i have 69 inputs and 9 outputs which I need to monitor a combine of them in each step).
I read about access type in vhdl, but as i have seen it's juts for variables, and i am using signal and 'last_event attribute which make impossible for me to jump to variables. and the entry parameter of the procedure is a signal.
Thanks for all. :-o
The INPUTS_OUTPUT signal will never change length, as it is a signal. So you can just read INPUTS_OUTPUT'length inside the procedure to see how long it is.
Is it possible to use VHDL style entity instantiation to include a Verilog module in a VHDL design?
I realize that I can accomplish this if I treat the Verilog module as a component and instantiate the component.
Thanks
Grab from my collection of code:
module sync_fifo
#(parameter WIDTH = 8, // width in bits
L2DEPTH = 4, // Log 2 Depth, 4=16 deep
REGFLAGS = 1 // Full, empty are registered
)
(
input clk, // system clock
input reset_n, // A-synchronous low reset/clear
input enable, // clock gating
input clear, // Synchronous clear
input write, // write FIFO
input [WIDTH-1:0] wdata, // write data
input read, // read FIFO
output [WIDTH-1:0] rdata, // read data
output reg empty, // FIFO is empty
output reg full, // FIFO is full
output reg [L2DEPTH:0] level // Fill level
);
outp_fifo : sync_fifo
generic map(
WIDTH => 10, -- Byte + user + last
L2DEPTH => 7, -- 128 deep
REGFLAGS=> 1
)
port map
(
clk => ACLK, -- system clock
reset_n => ARESETN, -- A-synchronous low reset/clear
enable => BIT_1 , -- clock gating
clear => BIT_0 , -- Synchronous clear
write => package_byte_en, -- write FIFO
wdata => outp_fifo_wt_data_and_meta , -- write data
read => outp_fifo_read , -- read FIFO
rdata => outp_fifo_rd_data_and_meta , -- read data
empty => outp_fifo_empty, -- FIFO is empty
full => outp_fifo_full, -- FIFO is full
level => open -- Fill level
);
Post edit:
And you can only do that with a competent declaration:
COMPONENT sync_fifo IS
generic(
WIDTH : integer := 8;
L2DEPTH : integer := 8;
REGFLAGS : integer := 1
);
PORT (
clk : in STD_LOGIC; -- system clock
reset_n: in STD_LOGIC; -- A-synchronous low reset/clear
enable : in STD_LOGIC; -- clock gating
clear : in STD_LOGIC; -- Synchronous clear
write : in STD_LOGIC; -- write FIFO
wdata : in STD_LOGIC_VECTOR(WIDTH-1 downto 0); -- write data
read : in STD_LOGIC; -- read FIFO
rdata : out STD_LOGIC_VECTOR(WIDTH-1 downto 0); -- read data
empty : out STD_LOGIC; -- FIFO is empty
full : out STD_LOGIC; -- FIFO is full
level : out STD_LOGIC_VECTOR(L2DEPTH downto 0) -- Fill level
);
END COMPONENT;
Although the answer seemed to be "no" 3 years ago when this was asked, I have found the answer in practice to be "yes you can", but with some caveats. I confirmed this using Mentor ModelSim and Xilinx Vivado.
I don't know if something about the LRM changed or if the tool vendors just decided to support it anyway. I'm using VHDL-2008, so I suspect the latter.
Here's an instance I have in one of my test files. VerFlopX is a Verilog module.
VerFlopX8c: entity work.VerFlopX (rtl)
generic map (SIZE => 8) --integer:=1
port map (
Clk => Clk, --in wire
D => D16(15 downto 8) , --in wire[(SIZE-1):0]
Q => Q16(15 downto 8)); --out wire[(SIZE-1):0]
The caveats:
Note that architecture "rtl" is specified. This can (and probably should) be omitted. But it still works, which seems weird since Verilog modules don't have architectures.
You cannot associate the Q output with open. ModelSim will return an error. If you change to a component instantiation, having a formal output connected to open is fine.
I am currently in the middle of a project where I am attempting to design a single cycle cpu. I am doing this without any pipe-lining, since that would greatly add to the complexity of the design. I am simply taking baby steps as I learn this. I find myself stuck at this portion where I am simply attempting to code a Program Counter(PC) using previously made components.
The model of my design looks like this picture here. Sorry, no idea why it came out dark, but if you click on it it shows correctly. The PC and theMUX are both 32 bit components, so I assume the adder is as well.
Here is the code I have been given, my implementation begins at the begin statement on line 41.
Pay no attention to it for now, its just a bunch of random gibberish I was attempting.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
---------------------------------------------------
entity pc_update is
port( clk: in std_logic; -- clock
incH_ldL: in std_logic; -- increment PC = PC + 4 when high,
-- load PCInput when low
PCInput: in std_logic_vector(31 downto 0); -- external input for PC
InstrAddr: out std_logic_vector(31 downto 0) ); -- instruction address
end entity pc_update;
----------------------------------------------------
architecture pc_update_arch of pc_update is
component register32 is
port( clr: in std_logic; -- async. clear
clk: in std_logic; -- clock
ld: in std_logic; -- load
D: in std_logic_vector(31 downto 0); -- data input
Q: out std_logic_vector(31 downto 0) ); -- data output
end component register32;
component mux2to1_32 is
port( sel: in std_logic; -- selection bit input
X0: in std_logic_vector(31 downto 0); -- first input
X1: in std_logic_vector(31 downto 0); -- second input
Y: out std_logic_vector(31 downto 0)); -- output
end component mux2to1_32;
signal PC_current: std_logic_vector(31 downto 0); -- the current state of PC reg
signal PC_add_4: std_logic_vector(31 downto 0); -- output from the adder
signal PC_next: std_logic_vector(31 downto 0); -- output from the MUX
begin
PC: register32 Port Map(
clk, Q, clr, D);
MUX: mux2to1_32 Port Map(
X0,sel,X1,Y);
process (incH_ldL)
begin
wait until (clk = '1');
if incH_1dL = '0' then
InstrAddr <= X0;
else InstrAddr <= X1;
end if;
end process;
end architecture pc_update_arch;
I am fairly new to this so I have only a faint idea of how signals work, and no idea how I am supposed to implement the components into the design. I am also confused that I wasnt asked to build the adder ahead of time. Is it now necessary to use it as a component im guessing?
Anyhow, I have attempted different things that stumbled upon searching, such as the port mapping you see. But I always get some sort of error, currently the error im receiving is that objects Q, clr, and D are used but not declared. How do I declare them?
If I get rid of those statements, the error simply repeats for objects X0, X1, and Y.
Any help in the right direction would be greatly appreciated. Thanks guys!
Also, just in case you need them,
The register
library ieee ;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
---------------------------------------------------
entity register32 is port(
clr: in std_logic; -- async. clear
clk: in std_logic; -- clock
ld: in std_logic; -- load
D: in std_logic_vector(31 downto 0); -- data input
Q: out std_logic_vector(31 downto 0) ); -- data output
end entity register32;
----------------------------------------------------
architecture register32_arch of register32 is
begin
process(clk, clr)
begin
if clr = '1' then
q <= x"00000000";
elsif rising_edge(clk) then
if ld = '1' then
q <= d;
end if;
end if;
end process;
END register32_arch;
and the MUX
library ieee ;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
---------------------------------------------------
entity mux2to1_32 is
port( sel: in std_logic; -- selection bit input
X0: in std_logic_vector(31 downto 0); -- first input
X1: in std_logic_vector(31 downto 0); -- second input
Y: out std_logic_vector(31 downto 0)); -- output
end entity mux2to1_32;
----------------------------------------------------
architecture mux2to1_32_arch of mux2to1_32 is
begin
Y <= X1 when (SEL = '1') else X0;
end architecture mux2to1_32_arch;
EDIT
Ok, NO idea if I did this correctly, but I rewrote the portmaps. I was having errors of port names (sel, clk, X0, X1..etc) being "used but not initialized. So that is why clr, clk and ld have initial values. Once again, no idea if that is correct, but it made the errors go away. I also realized I never added the register32 and mux2to1_32 VHDL files to my project, and after doing so got rid of the other errors I was having.
So as stands, the code compiles, I have included in the project a VWF simulation file for testing, but I KNOW the results are gonna be incorrect.
I dont know everything that is wrong yet, but I know I need to do something with PC_add_4. THis value needs to basically be (PC_current + 4), but Im not sure how to do this.
Here is the updated portion of code(everything else is the same)
PC: register32 Port Map(
clr => '0',
clk => '0',
ld => '1',
Q => PC_current,
D => PC_next
);
MUX: mux2to1_32 Port Map(
sel => incH_ldL,
X0 => PCInput ,
X1 => PC_add_4,
Y => PC_next
);
process (incH_ldL)
begin
if (rising_edge(clk)) then
if incH_ldL = '0' then
InstrAddr <= PC_current;
else InstrAddr <= PC_add_4;
end if;
end if;
end process;
And, in case they help, my list of errors..im guessing the pin related errors are because I dont have any hardware assignments made yet.
Warning (10541): VHDL Signal Declaration warning at pc_update.vhd(38): used implicit default value for signal "PC_add_4" because signal was never assigned a value or an explicit default value. Use of implicit default value may introduce unintended design optimizations.
Warning (10492): VHDL Process Statement warning at pc_update.vhd(61): signal "clk" is read inside the Process Statement but isn't in the Process Statement's sensitivity list
Warning: Output pins are stuck at VCC or GND
Warning: Design contains 34 input pin(s) that do not drive logic
Warning: Found 32 output pins without output pin load capacitance assignment
Warning: The Reserve All Unused Pins setting has not been specified, and will default to 'As output driving ground'.
Warning: Can't generate programming files because you are currently using the Quartus II software in Evaluation Mode
Warning: No paths found for timing analysis
Critical Warning: No exact pin location assignment(s) for 66 pins of 66 total pins
SECOND EDIT
So yeah I fixed up my code by adding
PC_add_4 <= (PC_current + 4 );
after the port mappings, and adding "clk" to the process sensitivity list.
However my waveforms in my simulation are still wrong I believe, as shown here.
It appears to be treating incH_lDL as a clear signal, rather than simply passing PCInput to InstrAddr. This is most likely due to my setting of it to a default '0' in the port map. I did this earlier because it was giving me "used but not declared" errors. Ill try messing with it and post my findings.
Third EDIT
I have edited my code as such:
process (incH_ldL, clk)
begin
if rising_edge(clk) then
if (incH_ldL = '0') then
InstrAddr <= PCInput ;
else InstrAddr <= PC_add_4;
end if;
end if;
end process;
My simulation now shows that when incH_lDL = 0, PCInput is loaded into InstrAddr, however, when incH_lDL = 1, it simply loads the value '4', and doesnt increment at the start of every clock cycle like its supposed to...I need to make use of PC_current, but I am not sure how....sicne you cant assign one signal to another like "PC_current <= PCInput". I will try some more things,in the mean time, any pointers would be greatly appreciated.
FOURTH EDIT
THanks to anyone still reading this, and bearing through all the reading.
I have attempted to use PC_next and PC_current in my implementation, but have run into "multiple constant drivers for net "PC_next" errors.
MY process code:
process (incH_ldL, clk, PC_next, PC_current)
begin
if rising_edge(clk) then
if (incH_ldL = '0') then
PC_next <= PCInput;
else PC_next <= PC_add_4;
end if;
end if;
InstrAddr <= PC_current;
end process;
I am aware that this error comes when these assignments are made within loops? I am truly at a loss here at what to try next.
Your port maps in the first code need to be ported to signals. You are placing the port names of the components in the port map, which is incorrect. What you would like to do is create signals that can connect those components, and place them in the port map fields instead (to match the connections in your image).
If I have the following VHDL-200X architecture:
architecture my_arc of my_entity is
signal test_char : std_logic_vector(7 downto 0);
signal test_char_c : character;
signal test_char_i : integer;
begin
test_char <= "01001010";
test_char_i <= to_integer(unsigned(test_char));
test_char_c <= character'val(test_char_i);
end architecture my_arc;
...and simulate it (in Xilinx iSim 14.1), test_char_c does not change from its initial value of NUL even though test_char_i takes the value 74. If, however, I replace the last line in the architecture with:
process(test_char_i)
begin
test_char_c <= character'val(test_char_i);
end process;
...then test_char_c takes on the value J as I'd expect.
I thought that a bare signal assignment will be updated concurrently if any signal on the right hand side changes. In other words, it's equivalent to a process that is sensitive to all signals involved in the assignment.
Why doesn't test_char_c get updated in the first instance?
Edit: Changing test_char_i to a natural doesn't change the result.
At initialisation time, test_char_i has the value integer'low, which doesn't map to a character - Modelsim 10.0 reports:
# ** Fatal: (vsim-3390) Result ?(-2147483648) of attribute 'VAL is out of range NUL (0) to 'ΓΏ' (255).
# Time: 0 ns Iteration: 0 Process: /my_entity/line__15 File: attr.vhd
# Fatal error in Architecture my_arc at attr.vhd line 15
#
If I make test_char_i a natural, so that it initialises to 0, things work as you expect (in Modelsim at least, haven't tried iSim)
I thought that a bare signal assignment will be updated concurrently if any signal on the right hand side changes. In other words, it's equivalent to a process that is sensitive to all signals involved in the assignment.
That's correct.
Why doesn't test_char_c get updated in the first instance?
It does.
A Minimal, Complete, and Verifiable example with a monitor process that will report all value updates on test_char_c:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity my_entity is
end entity;
architecture my_arc of my_entity is
signal test_char : std_logic_vector(7 downto 0);
signal test_char_c : character;
signal test_char_i : natural; -- integer;
begin
test_char <= "01001010";
test_char_i <= to_integer(unsigned(test_char));
test_char_c <= character'val(test_char_i);
process (test_char_c)
begin
report "test_char_c = " & character'image(test_char_c);
end process;
end architecture my_arc;
Note the change to the declaration of test_char_i to overcome the default initial value (INTEGER'LOW) causing a bound check failure as reported by Martin Thompson.
This was analyzed, elaborated and simulated using a -1993 compliant VHDL tool:
ghdl -r my_entity
../../src/ieee/numeric_std-body.v93:2098:7:#0ms:(assertion warning): NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0
my_entity.vhdl:19:9:#0ms:(report note): test_char_c = nul
my_entity.vhdl:19:9:#0ms:(report note): test_char_c = 'J'
The assertion warning from package numeric_std is caused by the test_char default intial value of "UUUUUUUU".
The first reported test_char_c value is the NUL you reported and occurs because the initial value of test_char_i is 0 (mapping to NUL).
The second is in response to the concurrent simple signal assignment to test_char resulting in an update of test_char_i in turn resulting in an update of test_char_c(and resuming the monitor process). It reflects the assigned bit string to test_char with a value x"4A" (corresponding to the character 'J').
If instead of the shown monitor process you were to have an assertion statement of the form:
assert test_char_c /= NUL
report "test_char_c = " & character'image(test_char_c);
You'd find that only the first report statement is displayed because an assertion statement condition is evaluated and when found false asserts.
Likewise if the "/=" in the condition were changed to "=" only the second report statement would be displayed (showing 'J').
Without providing a MCVe your problem can't be duplicated (or blamed on the then nascent ISIM).