I'm using active-hdl to simulate my FPGA designs and I'd like to know if it's possible to use dynamically generated strings to represent my signals in the simulator. For example, let's say I have a 4-bit std_logic_vector containing an op-code, I'd like the simulator to display the op-code strings "nop", "add", "sub" etc instead of the vector value.
I tried declaring a custom enumeration type at first but quickly discovered that you don't get to choose the values of the individual elements. My next solution was to use the enumeration for simulation display only and convert with a translation function:
type op_code_type is (nop, add, sub, unknown); -- not in order
signal op_code_str: op_code_type;
signal op_code: std_logic_vector(3 downto 0);
function to_string(op_code : std_logic_vector(3 downto 0))
return op_code_type is
begin
case op_code is
when "0000" => return nop;
when "0010" => return add;
when "0011" => return sub;
when others => return unknown;
end case;
end to_string;
begin
----- testbench -----
process
begin
op_code <= "0000";
wait for 1ns;
op_code <= "0001";
wait for 1ns;
op_code <= "0010";
wait for 1ns;
op_code <= "0011";
wait for 1ns;
end process;
op_code_str <= to_string(op_code);
end architecture;
This actually works quite well, and is probably adequate for most things I want to do:
The main problem though is I'm stuck with string constants, so it'll be too impractical for more complex stuff like mov acc,x and all the many other variants a real-world design would have.
Are there ways to construct dynamic simulation identifiers like this? Or is it a fundamental limitation of HDLs?
In Modelsim, you can use virtual types and functions. For example, consider the following vector:
signal opcode : std_logic_vector(2 downto 0);
You can then at the Modelsim command line define a virtual type, such as:
virtual type {{0 nop} {1 load} {2 store} {3 invalid}} opcode_type
This is a type known only to the simulator. You can then create a virtual signal based on this type to convert the vector, such as:
virtual function {(opcode_type)opcode} opcode_str
Then wave opcode_str, and it will give you a custom formatted string..
I don't know if you can do the same with Active-HDL.
Now, as for doing it dynamically, the only possibility might be if the returned string is defined by a TCL function, such as:
# TCL code to read a file, or otherwise dynamically generate opcodes
# returning the appropriately formatted virtual type
proc generate_opcode_type {} {
...
}
virtual type [generate_opcode_type] opcode_type
virtual function {(opcode_type)opcode} opcode_str
Then wave opcode_str.
For posterity, and at the request of #B. Go, here is my previous answer:
#Paebbels has it. We use this frequently, especially when doing post place-and-route simulations to convert state codes to their equivalent enumerated type. So for completeness, I'll show you how we do it. The example below considers a case where binary encoding is used. If trying to convert from grey or one-hot, things are a bit different. For one-hot, I tend to use a function.
Consider an 3-bit vector with associated names:
|-----------|----------|
| 000 | Idle |
| 001 | Start |
| 010 | Running |
| 011 | Paused |
| 100 | Done |
| 101 - 111 | Invalid |
|-----------|----------|
So, if you have a signal, such as:
signal opcode : std_logic_vector(2 downto 0);
Then you want to convert to an enumerated type, which will show up cleanly in your waveform viewer. First, create the enumerated type and associated signal:
type opcode_names is (idle, start, running, paused, done, invalid);
signal opcode_name : opcode_names;
Then it is a simple with/select:
with to_integer(unsigned(opcode)) select
opcode_name <= idle when 0,
start when 1,
running when 2,
paused when 3,
done when 4,
invalid when others;
Though if you have a complete set, it is a bit simpler. Consider a 2-bit vector with names "idle, start, running, done".
type opcode_names is (idle, start, running, done);
signal opcode_name : opcode_names;
...
opcode_name <= opcode_names'image(to_integer(unsigned(opcode));
For more complex vectors with unusual, non-contiguous values, I typically use a function, such as:
signal opcode : std_logic_vector(31 downto 0);
type opcode_names is (idle, start, running1, running2, paused, done, invalid);
signal opcode_name : opcode_names;
function get_opcode_name(opcode : in std_logic_vector) return opcode_names is
variable ret : opcode_names;
begin
case to_integer(unsigned(opcode)) is
when 0 =>
ret := idle;
when 13 =>
ret := start;
when 87 =>
ret := running1;
when 131 =>
ret := running2;
when 761 =>
ret := paused;
when 3213 =>
ret := done;
when others =>
ret := invalid;
end case;
return ret;
end function get_opcode_name;
...
opcode_name <= get_opcode_name(opcode);
Related
What is the type enumeration in the VHDL?
where can I use it, to make the code shorter and more understandable?
for example, consider a bellow statement:
TYPE st_State IS (st_Idle, st_CheckHeader1, st_CheckHeader2, st_ReceiveData)
when must to use it.
Your example is only a declaration of a type with name st_State and this type contains four elements. Each element gets a number from 0 to Elements - 1. This is similar to a C typedef with an C enum.
Please check this explanation for more detailed information.
A typical application for this is a state machine to name the different states:
architecture Top_Arch of Top is
type State_Type is (S0, S1, S2, S3);
signal CurrentState : State_Type := S0;
begin
process(Clock)
begin
if(rising_edge(Clock)) then
case CurrentState is
when S0 => ...
when S1 => ...
when S2 => ...
when S3 => ...
end case;
end if;
end Top_Arch;
Using this method result in a more readable and cleaner code, but it is equivalent to this approach (untested):
architecture Top_Arch of Top is
signal CurrentState : INTEGER RANGE 0 to 3 := 0;
begin
process(Clock)
begin
if(rising_edge(Clock)) then
case CurrentState is
when 0 => ...
when 1 => ...
when 2 => ...
when 3 => ...
end case;
end if;
end Top_Arch;
NOTE: Check the range statement. You have to use it, because you have to declare each value for your state machine. So you have to use when others or reduce the integer to 2 bits only. Otherwise you have to declare 2^32 - 1 states.
So you need at least a type declaration with type <YourType> is ... to declare your custom type and a signal to use your type (CurrentState in the above example).
Enumerated types have many other uses than just states in state machines.
You can use them as index types in arrays, loop variables, etc. For example,
type channel is (R,G,B);
Colour : array(channel) of byte;
constant Black : Colour := (R => 0, G => 0, B => 0);
signal VGA_Out : Colour;
-- in a process
for c in channel loop
VGA_Out(c) <= A(c) + B(c); -- mix video signals A and B
end loop;
and so on
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.
I am getting some unexpected behavior when using the to_01 conversion function in VHDL-2008. My expectation would be that vector bits that can clearly be interpreted as high or low are mapped to '1' and '0' respectively. The remaining vector bits should be converted to '0' bits.
However, with the code depicted below, I get the whole vector converted to all '0's.
Is this behavior correct? Or is this a bug in the simulator software (ALDEC Riviera-PRO)?
Is there any IEEE function that meets my expectations or do I have to write my own function to achieve that?
library ieee;
use ieee.std_logic_1164.all;
entity test_to_01 is
end entity test_to_01;
architecture rtl of test_to_01 is
signal s_test_in : std_logic_vector(8 downto 0) := "UX01ZWLH-";
signal s_test_out : std_logic_vector(8 downto 0);
begin
s_test_out <= to_01(s_test_in);
end architecture rtl;
The observed behavior is the correct behavior. A little history about this follows.
In 2008, we propagated all of the strength reduction operations to all std_logic family packages. For better or worse, the historical implementation of to_01 comes from numeric_std and was implemented exactly as it is now. The following is an older implementation I was able to find on the web:
function TO_01(S : SIGNED ; xmap : STD_LOGIC:= '0') return SIGNED is
variable RESULT: SIGNED(S'length-1 downto 0);
variable bad_element : boolean := FALSE;
alias xs : SIGNED(s'length-1 downto 0) is S;
begin
for i in RESULT'range loop
case xs(i) is
when '0' | 'L' => RESULT(i):='0';
when '1' | 'H' => RESULT(i):='1';
when others => bad_element := TRUE;
end case;
end loop;
if bad_element then
assert NO_WARNING
report "numeric_std.TO_01: Array Element not in {0,1,H,L}"
severity warning;
for i in RESULT'range loop
RESULT(i) := xmap; -- standard fixup
end loop;
end if;
return RESULT;
end TO_01;
One of the prime directives of the VHDL WG is to not break old code. In this case it looks like this objective put forward an implementation that perhaps is less desirable.
If you want something different, you can always put it forward for the next revision of the standard. It would have to have a different name. Note we are currently closing on VHDL-2018 now, so it would be the revision after that.
Note that IEEE P1076 WG is an individual based working group. This means experienced users, such as yourself, are participating. Typically the amount of work done in a standards revision is overwhelming. As a result, we always need more active participants. Particularly working on the packages. See eda-twiki.org and http://www.eda-twiki.org/cgi-bin/view.cgi/P1076/WebHome
I found a workaround:
s_test_out <= to_stdlogicvector(to_bitvector(s_test_in));
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.
I've some problem with my synthesis tool. I'm writing a module and I'm tryng to make it parametric and scalable. In my design I've a FSM and some counters. The counters have a parametric width ( they are function of the width of the datapath ). The problem is that I'm using that counter to drive a case statements. The synthesizer gives me back this error :
2049990 ERROR - (VHDL-1544) array type case expression must be of a locally static subtype
I've also tried to use subtype, but it doesnt work. The declaration is :
constant LENGTH_COUNTER_WORD : integer := integer(ceil(log2(real(WIDTH_DATA/WIDTH_WORD))));
subtype type_counter_word is std_logic_vector( LENGTH_COUNTER_WORD - 1 downto 0);
signal counter_word : std_logic_vector( LENGTH_COUNTER_WORD - 1 downto 0);
The case :
case type_counter_word'(counter_word) is
when (others => '1') =>
do_stuff();
when others =>
do_other_stuff();
end case;
I cannot switch to VHDL-2008. I've read I can use variable, but I'd like to find a different solution, if it exists. I cannot imagine there isn't any way to give parameters to synthesizer before the synthesis.
This is fixed in VHDL-2008. You can only work around it in earlier standards by using cascaded if statements (with the attendant priority logic). Variables don't make a difference when determining if choices are locally static.
I'm not sure how complicated your do_stuff() and do_other_stuff() operations are, but if you are just doing simple signal assignments, you could look into the and_reduce() function in the ieee.std_logic_misc library.
As an example:
output <= '1' when and_reduce(type_counter_word'(counter_word)) = '1' else '0';
Otherwise, as Kevin's answer suggests, a process block using if statements might be your best option.
About the time of Kevin's good enough answer, I had written this to demonstrate:
library ieee;
use ieee.std_logic_1164.all;
use ieee.math_real.all;
entity counterword is
generic (
WIDTH_DATA: positive := 16;
WIDTH_WORD: positive := 8
);
end entity;
architecture foo of counterword is
constant LENGTH_COUNTER_WORD : integer :=
integer(ceil(log2(real(WIDTH_DATA/WIDTH_WORD))));
subtype type_counter_word is
std_logic_vector( LENGTH_COUNTER_WORD - 1 downto 0);
signal counter_word : std_logic_vector( LENGTH_COUNTER_WORD - 1 downto 0);
procedure do_stuff is
begin
end;
procedure do_other_stuff is
begin
end;
begin
UNLABELLED:
process (counter_word)
begin
-- case type_counter_word'(counter_word) is
-- when (others => '1') =>
-- do_stuff;
-- when others =>
-- do_other_stuff;
-- end case;
if counter_word = type_counter_word'(others => '1') then
do_stuff;
else
do_other_stuff;
end if;
end process;
end architecture;
Note because type_counter_word is a subtype you can provide the subtype constraints in a qualified expression for the aggregate:
if counter_word = type_counter_word'(others => '1') then
From IEEE Std 1076-2008:
9.3.5 Qualified expressions
A qualified expression is a basic operation (see 5.1) that is used to explicitly state the type, and possibly the subtype, of an operand that is an expression or an aggregate.
This example analyzes, elaborates and simulates while doing nothing in particular. It'll call the sequential procedure statement do_other_stuff, which does nothing.
(For do_stuff and do_other stuff, empty interface lists aren't allowed).