I have a bit vector like this
subtype alarms_type : std_logic_vector(1 to 8)
signal alarms : alarms_type := (others => '0');
and the purpose of each bit is defined like this
constant sys1_temp_hi_al : integer := 1;
constant sys1_temp_lo_al : integer := 2;
constant sys1_crnt_hi_al : integer := 3;
constant sys1_cnrt_lo_al : integer := 4;
constant sys2_temp_hi_al : integer := 5;
constant sys2_temp_lo_al : integer := 6;
constant sys2_crnt_hi_al : integer := 7;
constant sys2_cnrt_lo_al : integer := 8;
I would like to be able to access subsets of the alarms_type with a short, readable notation. For example
alarms(SYS2_ALS) <= (others => '0'); -- set multiple values
temp_alarm <= or(alarms(TEMP_ALS)); -- or together multiple values to a std_logic
temp_alarms <= alarms(TEMP_ALS)); -- extract a subset to a suitably sized vector
alarms(TEMP_ALS) := (others => '1'); -- set (or clear) multiple disjoint values
I would like to know if the alias keyword will help me, like this:
alias HI_ALARMS is sys1_temp_hi_al|sys1_crnt_hi_al|sys2_temp_hi_al|sys2_crnt_hi_al;
alias LO_ALARMS is sys1_temp_lo_al|sys1_crnt_lo_al|sys2_temp_lo_al|sys2_crnt_lo_al;
alias TEMP_ALS is sys1_temp_hi_al|sys1_temp_lo_al|sys2_temp_hi_al|sys2_temp_lo_al;
alias CRNT_ALS is sys1_crnt_hi_al|sys1_crnt_lo_al|sys2_crnt_hi_al|sys2_crnt_lo_al;
alias SYS1_ALS is sys1_temp_hi_al|sys1_crnt_hi_al|sys2_temp_hi_al|sys2_crnt_hi_al;
alias SYS2_ALS is sys1_temp_hi_al|sys1_crnt_hi_al|sys2_temp_hi_al|sys2_crnt_hi_al;
It appears from the Alias chapter of the Ashenden book that this is not how an alias should be used, but hopefully I am missing something.
If there is a correct way to use alias to achieve my goal, please provide examples.
Alternatively, if there is a different or better way using constant or some other aspect of VHDL, please show me how.
Or just tell me that it can't be done, and if it is appropriate, please confirm that I could write functions to get and procedures to set the different subsets.
If I use functions and procedures, is there a cleaner notation than this? (I may need lots of this functions and procedures).
function get_temp_als(alarms : alarms_type)
return alarms(sys1_temp_hi_al) & alarms(sys1_temp_lo_al) & alarms(sys2_temp_hi_al) & alarms(sys2_temp_lo_al);
function get_combined_temp_als(alarms : alarms_type)
return alarms(sys1_temp_hi_al) or alarms(sys1_temp_lo_al) or alarms(sys2_temp_hi_al) or alarms(sys2_temp_lo_al);
procedure set_temp_als(alarms: alarm_type, value:std_logic)
begin
alarms(sys1_temp_hi_al) <= value;
alarms(sys1_temp_lo_al) <= value;
alarms(sys2_temp_hi_al) <= value;
alarms(sys2_temp_lo_al) <= value;;
end;
I would like this to work with VHDL 2002 and it is intended for synthesis.
Aliases are new names for existing named entities not new entities.
Alternatively, if there is a different or better way using constant or some other aspect of VHDL, please show me how.
There's the basic issue where it isn't clear what you're going to do with information, lacking a minimal, complete, and verifiable example you're trying to make more 'elegant'.
It is possible to use constants to specify alarms_type indexes:
library ieee;
use ieee.std_logic_1164.all;
entity alarming is
end entity;
architecture fum of alarming is
-- subprograms, constants and type declarations could be in a package
subtype alarm_range is integer range 1 to 8;
subtype alarms_type is std_logic_vector(alarm_range);
signal alarms: alarms_type := (others => '0');
constant sys1_temp_hi_al : integer := 1;
constant sys1_temp_lo_al : integer := 2;
constant sys1_crnt_hi_al : integer := 3;
constant sys1_crnt_lo_al : integer := 4;
constant sys2_temp_hi_al : integer := 5;
constant sys2_temp_lo_al : integer := 6;
constant sys2_crnt_hi_al : integer := 7;
constant sys2_crnt_lo_al : integer := 8;
type index is array (natural range <>) of alarm_range;
constant HI_ALARMS: index := (sys1_temp_hi_al, sys1_crnt_hi_al,
sys2_temp_hi_al, sys2_crnt_hi_al);
constant LO_ALARMS: index := (sys1_temp_lo_al, sys1_crnt_lo_al,
sys2_temp_lo_al, sys2_crnt_lo_al);
constant TEMP_ALS: index := (sys1_temp_hi_al, sys1_temp_lo_al,
sys2_temp_hi_al, sys2_temp_lo_al);
constant CRNT_ALS: index := (sys1_crnt_hi_al, sys1_crnt_lo_al,
sys2_crnt_hi_al, sys2_crnt_lo_al);
constant SYS1_ALS: index := (sys1_temp_hi_al, sys1_temp_lo_al,
sys1_crnt_hi_al, sys1_crnt_lo_al);
constant SYS2_ALS: index := (sys2_temp_hi_al, sys2_temp_lo_al,
sys2_crnt_hi_al, sys2_crnt_lo_al);
function get_alarm (alarm: alarms_type; indx: alarm_range)
return std_logic is
begin
return alarm(indx);
end function;
function set_alarm (alarm: alarms_type; indx: alarm_range)
return alarms_type is
variable ret_val: alarms_type := alarm;
begin
ret_val(indx) := '1';
return ret_val;
end function;
function get_alarms (alarm: alarms_type; alarm_indx: index)
return std_logic_vector is -- doesn't look real useful.
variable ret_val: std_logic_vector(alarm_indx'range);
begin
for i in alarm_indx'range loop
ret_val(i) := alarm(alarm_indx(i));
end loop;
return ret_val;
end function;
-- instead:
function select_alarms (alarm: alarms_type; alarm_indx: index)
return alarms_type is
variable ret_val: alarms_type := (others => '0');
begin
for i in alarm_indx'range loop
ret_val(alarm_indx(i)) := alarm(alarm_indx(i));
end loop;
return ret_val;
end function;
-- which returns a mask selected alarms_type value that allows the use of a
-- single alarm service routine.
function set_alarms (alarm: alarms_type; alarm_indx: index)
return alarms_type is
variable ret_val: alarms_type := (others => '0');
begin
for i in alarm_indx'range loop
ret_val(i) := alarm(alarm_indx(i));
end loop;
return ret_val;
end function;
procedure report_alarms (alarm: in alarms_type) is
type name_array is array (alarm_range) of string (1 to 15);
constant alarm_name: name_array := (
"sys1_temp_hi_al", "sys1_temp_lo_al",
"sys1_crnt_hi_al", "sys1_crnt_lo_al",
"sys2_temp_hi_al", "sys2_temp_lo_al",
"sys2_crnt_hi_al", "sys2_crnt_lo_al"
);
begin
for i in alarm_range loop
if alarm(i) = '1' then
report "alarm " & alarm_name(i) & " is set";
end if;
end loop;
end procedure;
begin
alarms <= select_alarms("11111111", TEMP_ALS) after 5 ns;
LABELED:
process (alarms)
begin
report_alarms(alarms);
end process;
end architecture;
And
ghdl -r alarming
alarming.vhdl:102:17:#5ns:(report note): alarm sys1_temp_hi_al is set
alarming.vhdl:102:17:#5ns:(report note): alarm sys1_temp_lo_al is set
alarming.vhdl:102:17:#5ns:(report note): alarm sys2_temp_hi_al is set
alarming.vhdl:102:17:#5ns:(report note): alarm sys2_temp_lo_al is set
tells us I can set all the temperature alarms using alarms selected by the set of temperature alarm indexes.
By providing a second parameter for selecting a set of alarms by name the number of subprograms drops (the alternative is to declare a type for all the different sets of alarms and provide subprograms for them all).
You are correct in that aliases do not work like that. They are an alias to a single object (or slice thereof) and cannot be used to make new compounded objects like you might do with a pointer in another language.
A procedure is likely the only way you can achieve this. The alarms paramter should be a signal and mode out:
procedure set_temp_als(signal alarms: out alarm_type; value:std_logic)
begin
alarms(sys1_temp_hi_al) <= value;
alarms(sys1_temp_lo_al) <= value;
alarms(sys2_temp_hi_al) <= value;
alarms(sys2_temp_lo_al) <= value;
end;
so you can assign the alarms with:
set_temp_als(alarms, '1');
Your functions are the right idea, but syntactically wrong:
function get_temp_als(alarms : alarms_type) return std_logic_vector is
begin
return alarms(sys1_temp_hi_al) & alarms(sys1_temp_lo_al) & alarms(sys2_temp_hi_al) & alarms(sys2_temp_lo_al);
end function;
signal some_other_slv : std_logic_vector(3 downto 0);
some_other_slv <= get_temp_als(alarms);
Related
I'm using QuartusII (version 13.0 or 17.0) and I'm trying to use a 2d-array with a specific type in my entity's generic port, so I can instantiate a MEMORY bloc with one of that 2d-array columns.
In codes, it would look like this :
I have 2 packages, the 1st one is to handle the specific type (fixed type) in which I have a function that converts INTEGER to a configurable size FIXED_type :
library ieee;
use ieee.std_logic_1164.all;
-- PACKAGE DECLARATION
package fixed_pkg is
-- type fixed
type fixed is array (integer range <>) of std_logic;
-- creating a subtype so "fixed" type is easier to write
constant INT : positive := 5;
constant DEC : positive := 5;
subtype t_fixed is fixed(INT-1 downto -DEC);
-- function to convert INT to FIXED
function to_fixed (
val : integer;
l_size : natural;
r_size : natural
) return fixed;
end package fixed_pkg;
-- PACKAGE BODY
package body fixed_pkg is
-- function INT to FIXED
function to_fixed (
val : integer;
l_size : natural;
r_size : natural
) return fixed is
variable result : fixed (l_size-1 downto -r_size);
variable bit_val : std_logic := '0';
variable i_left : integer := val;
begin
result(-1 downto result'right) := (others => '0');
if (i_left < 0) then
bit_val := '1';
i_left := -(i_left)+1;
end if;
for i in 0 to result'left loop
if (i_left mod 2) = 0 then
result(i) := bit_val;
else
result(i) := not bit_val;
end if;
i_left := i_left / 2;
end loop;
return result;
end to_fixed;
end package body fixed_pkg;
The other package is to handle the MEMORY bloc. It contains types, one example of a 2d-array and a function to initialize a MEMORY from a 2d-array :
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.fixed_pkg.all;
-- PACKAGE DECLARATION
package some_pkg is
-- rom type
type t_ROM is array (natural range <>) of t_fixed;
-- 2d-array type
type t_2darray is array (natural range <>, natural range <>) of t_fixed;
-- Example of 2d-array
constant ex_2darray : t_2darray (0 to 2, 0 to 2)
:= ((to_fixed(0, INT, DEC), to_fixed(1, INT, DEC), to_fixed(2, INT, DEC)),
(to_fixed(3, INT, DEC), to_fixed(4, INT, DEC), to_fixed(5, INT, DEC)),
(to_fixed(6, INT, DEC), to_fixed(7, INT, DEC), to_fixed(8, INT, DEC))
);
-- function to initialize a ROM bloc
-- <input> init_2darray : 2d array that contains values
-- <input> column_index : selected column in init_2darray that will be copied
-- <input> ROM_size : Size of the return ROM values
-- <output> values from the column_index-th of init_2darray
function init_ROM (
init_2darray : t_2darray;
column_index : natural;
ROM_size : positive
) return t_ROM;
end package some_pkg;
-- PACKAGE BODY
package body some_pkg is
function init_ROM (
init_2darray : t_2darray;
column_index : natural;
ROM_size : positive) return t_ROM is
-- rom values that will be returned
variable ROM_val : t_ROM(0 to ROM_size-1) := (others => to_fixed(0, INT, DEC));
begin
-- filling ROM_val with values from the column_index-th in init_2darray
for addr in 0 to ROM_size-1 loop
ROM_val(addr) := init_2darray(addr, column_index);
end loop;
return ROM_val;
end init_ROM;
end package body some_pkg;
ANNND finally, here is an exemple of instanciation of a bloc MEMORY(ROM) :
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
library work;
use work.some_pkg.all;
use work.fixed_pkg.all;
entity ROM is
generic(
ROM_size : positive := 3;
init_2darray : t_2darray := ex_2darray;
column_index : natural := 0
);
port(
clk : in std_logic;
rdAdd : in natural range 0 to ROM_size-1;
out_data : out t_fixed
);
end ROM;
architecture arch of ROM is
-- ROM instantiation/initialization
constant ROMbloc : t_ROM(0 to ROM_size-1) := init_ROM(init_2darray, column_index, ROM_size);
begin
-- READ PROCESS
process(clk)
begin
if rising_edge(clk) then
out_data <= ROMbloc(rdAdd);
end if;
end process;
end arch;
MY PROBLEM :
In that example of codes I gave, when I try to compile, Quartus throws an "Internal Error". BUT, when I'm using only INTEGER type for the values, I have no problem ...
So I don't really understand what is wrong with my codes... here is the quartus report :
screenshot Quartus Error (Quartus 17.0) when "start compilation"
NB : I'm not using the existing standard fixed package, it's my constraint
Does anyone know how can I handle this problem ?
Thanks for your help !
Apparently, using 2d-array as parameter in the entity generic port works as long as the value type used in the 2d-array is standard.
So the error from quartus appeared when I'm using my function that converts INTEGER to FIXED type in my 2d-array. I found a way to overcome it and I'm not sure it is appropriate but I succeed to make my design compile without error.
In my package, I just keep my values in my 2d-array as INTEGER type like:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.fixed_pkg.all;
-- PACKAGE DECLARATION
package some_pkg is
-- rom type
type t_ROM is array (natural range <>) of t_fixed;
-- 2d-array type
type t_2darray is array (natural range <>, natural range <>) of INTEGER; -- still integer
-- Example of 2d-array
constant ex_2darray : t_2darray (0 to 2, 0 to 2)
:= ((0, 1, 2),
(3, 4, 5),
(6, 7, 8)
);
-- function to initialize a ROM bloc
-- <input> init_2darray : 2d array that contains values
-- <input> column_index : selected column in init_2darray that will be copied
-- <input> ROM_size : Size of the return ROM values
-- <output> values from the column_index-th of init_2darray
function init_ROM (
init_2darray : t_2darray;
column_index : natural;
ROM_size : positive
) return t_ROM;
end package some_pkg;
Then when I initialize my ROM, I do the conversion to FIXED type through my initialization function:
-- PACKAGE BODY
package body some_pkg is
function init_ROM (
init_2darray : t_2darray;
column_index : natural;
ROM_size : positive) return t_ROM is
-- rom values that will be returned
variable ROM_val : t_ROM(0 to ROM_size-1) := (others => to_fixed(0, INT, DEC));
begin
-- filling ROM_val with *converted* values from the column_index-th in init_2darray
for addr in 0 to ROM_size-1 loop
ROM_val(addr) := to_fixed(init_2darray(addr, column_index), INT, DEC); -- I'M DOING THE CONVERSION HERE
end loop;
return ROM_val;
end init_ROM;
end package body some_pkg;
Finally, my goal was to be able to instantiate different ROM components from the 2d-array. In a higher level entity, it would look like:
-- [...]
gen_ROM : for i in 0 to 2 generate
memory : ROM
generic map (
ROM_size : positive := 3;
init_2darray : t_2darray := ex_2darray;
column_index : natural := i
)
port map (
clk => clk,
rdAdd => rdAdd_array(i) -- where rdAdd_array is declared before hand
out_data => out_data_array(i) -- where out_data_array is declared before hand
);
end generate gen_ROM;
-- [...]
where 3 ROM components would be initialized with the columns from the 2d-array.
I hope it will help, just in case someone would face this kind of problem (even if it is specific, I agree).
Consider this table in an MSSQL database:
CREATE TABLE dbo.TESTPAR
(
ID INTEGER NOT NULL,
YR VARCHAR(50) NULL
)
I have a TFDQuery with command text:
insert into TESTPAR
(ID,YR)
values(:ID,cast(:YR as varchar(4)))
This has two ftInteger ptInput parameters
Executing it with
procedure TFrmCastAsVarchar.BtnTestInsertClick(Sender: TObject);
begin
Inc(FLastID);
FDQuery2.Params[0].AsInteger := FLastID;
FDQuery2.Params[1].AsInteger := 2018;
try
FDQuery2.ExecSQL;
except
on E:Exception do ShowMessage(E.Message);
end;
end;
gives an error EMSSQLNativeException Arithmetic overflow converting numeric to data type varchar when Mapping for dtBCD and dtFmtBCD fields is active:
procedure TDM.SetBCDMapRules;
// For (Fmt)BCD data types. Called from SetOracleMapRules/SetMSSQLMapRules
begin
with FDConnection.FormatOptions.MapRules.Add do
begin // Convert numeric data types with scale=0 and precision<=10 to a 32-bit integer
PrecMax := 10;
PrecMin := 0;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtBCD;
TargetDataType := dtInt32;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Do the same for those that might return as dtFmtBCD instead of dtBCD
PrecMax := 10;
PrecMin := 0;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtFmtBCD;
TargetDataType := dtInt32;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Convert numeric data types with scale=0 and precision>10 to a 64-bit integer
PrecMin := 11;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtBCD;
TargetDataType := dtInt64;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Idem dtFmtBCD
PrecMin := 11;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtFmtBCD;
TargetDataType := dtInt64;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // All other dtBCD types (notably scale <> 0) should return as float
SourceDataType := dtBCD;
TargetDataType := dtDouble;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Idem dtFmtBCD
SourceDataType := dtFmtBCD;
TargetDataType := dtDouble;
end;
end;
(How) can I change the SQL to fix this?
Alternatively, is there something weird in my mapping rules that could be fixed? I'm surprised this has an influence at all.
This is of course just a basic example. The real script concatenates other strings to the cast() to arrive at a varchar value to put into the varchar field.
Not using the BCD mappings will give other issues (e.g. with DECIMAL field types).
Changing the table structure for the client "is not optimal" ;-)
I have tested this using a lot of different ODBC/native drivers.
This is Delphi Tokyo 10.2.3, Win32 app on Win7.
Sure, there is something wrong with your mapping (we've been at this before). For parameters, it is transformation of target into source. The Data Type Mapping topic says this:
In case of a command parameter, the rule defines a transformation of a
target data type, specified by an application, into a source data
type, supported by a driver.
So, in this case you have instructed FireDAC to transform 32-bit integer into decimal number, which when arrives to DBMS won't be just 4 chars long. If you want to fix this, then (ordered by reliability):
use proper data type in your table
stop using mapping rules in general
use proper parameter data type and pass value as it really is (so as string, not as integer)
cast the parameter value into integer like e.g. CAST(CAST(:YR AS INTEGER) AS VARCHAR(4))
I am to determine the length of each column in a 4x4 matrix. The lengths of each columns are counted from the bottom of each column upwards and are only counted from the initial '1' accessed onwards.
1110
0111
0110
0001
Column1=1, Column2=3, Column3=3, Column4=4 etc...
Does anyone have any ideas how I could do this? I have thus far attempted to produce a matrix alongside a function to extract each of the columns.
type col_mat is array (0 to 3) of std_logic;
type matrix is array (0 to 3, 0 to 3) of std_logic;
signal M: matrix;
signal temp_col : col_mat;
signal count : unsigned (1 downto 0):= "00";
function extract_col(x: matrix; column : integer) return col_mat is
variable ret: col_mat;
begin
for i in col_mat'range loop
ret(i) := x(i,column)
end loop;
return ret;
end function;
begin
if rising_edge(clk) then
temp_col<= extract_col(M, to_integer(count) );
count <= count+1;
end if;
end process;
You're describing a priority encoder and from the looks of it you're implying it operates in one clock, which can run into some clock rate limit based on the target device (assuming you'll synthesize).
A priority encode can be an if statement, a case statement, a loop statement with an exit (as Martin Zabel commented), describe one combinatorially with logic operators or with a conditional signal assignment.
For this particular use a loop statement is the most compact and one has been added to your process.
The following code is derived from your question, fleshed out into a Minimal, Complete, and Verifiable example.
The results are in terms of array type indexes (starting from 0).
I added a pipeline register for count named column as well as registers for a signal to specify a '1' was found (found_1) and the highest row value a '1' is found in (imaginatively named row):
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity column is
end entity;
architecture foo of column is
type col_mat is array (0 to 3) of std_logic;
type matrix is array (0 to 3, 0 to 3) of std_logic; -- (row,column)
-- signal M: matrix;
-- 1110
-- 0111
-- 0110
-- 0001
--
-- Column1=1, Column2=3, Column3=3, Column4=4 etc...
--
-- column0 = 0, column1 = 2 column2 = 2, column3 = 3
-- (matrix is defined so (0,0) is the upper left hand corner)
-- Looking for the highest column index occupied by a '1'
signal M: matrix := ( -- for demo provide matrix default value
('1','1','1','0'), -- row 0
('0','1','1','1'),
('0','1','1','0'),
('0','0','0','1') -- row 3
);
-- signal temp_col: col_mat;
signal count: unsigned (1 downto 0):= "00";
function extract_col(x: matrix; column: integer) return col_mat is
variable ret: col_mat;
begin
for i in col_mat'range loop
ret(i) := x(i,column); -- was missing semicolon
end loop;
return ret;
end function;
-- added signals:
signal clk: std_logic := '1'; -- rising_edge() requires 0 -> 1 trans
signal found_1: std_logic := '0';
signal column: unsigned (1 downto 0);
signal row: integer range 0 to 3;
signal mat_col: col_mat;
begin
UNLABELED:
process (clk)
variable temp_col: col_mat; -- made temp_col a variable, use immediately
begin
if rising_edge(clk) then
temp_col := extract_col(M, to_integer(count)); -- was signal
-- priority encoder: -- added loop
for i in temp_col'RIGHT downto temp_col'LEFT loop -- highest first
if temp_col(i) = '1' then
found_1 <= '1';
column <= count;
row <= i;
exit;
else
found_1 <= '0';
end if;
end loop;
mat_col <= temp_col; -- added
count <= count + 1;
end if;
end process;
CLOCK: -- Added clock process
process
begin
wait for 10 ns;
clk <= not clk;
if now > 90 ns then
wait;
end if;
end process;
end architecture;
And when simulated this gives:
I added mat_col because the tool I used doesn't do delta cycle waveforms and variables have no notion of time. temp_col was made a variable to allow it's value to be used in immediately after it's assigned (an updated signal value is not available in the current simulation cycle).
You can also see I provided a default value for M from your question.
I have a vector A that's 64bits long and I want the output B to equal 3 while A is 30-35 and zero elsewhere. I can't figure out the testbench to loop through the vector A as a bit. I've tried several different ways but only got 1/5 of the array to give any output at all. This is as far as I could get without syntax/compile errors.
Main code
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.ALL;
entity ent is
port(A:in std_logic_vector(5 downto 0);
B:out std_logic_vector(3 downto 0));
end ent;
architecture arch_ent of ent is
begin
with A select
B <= "0011" when "011110",
"0011" when "011111",
"0011" when "100000",
"0011" when "100001",
"0011" when "100010",
"0011" when "100011",
"0000" when others;
end arch_ent;
Testbench
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb is
end tb;
architecture arch_tb of tb is
component ent
port(A:in std_logic_vector(5 downto 0);
B:out std_logic_vector(3 downto 0));
end component;
signal A_tb: std_logic_vector(5 downto 0);
signal B_tb: std_logic_vector(3 downto 0);
begin
uut: entity ent port map(A=>A_tb, B=>B_tb);
tb: process
constant period: time := 20ns;
begin
for i in A_tb'range loop
A_tb <= std_logic_vector(to_unsigned(i,6));
wait for period;
assert (B_tb = "0011")
report "test failed" severity error;
end loop;
wait;
end process;
end arch_tb;
In the end I'm trying to plot out the waveform like this:
http://i10.photobucket.com/albums/a142/blargonblop/wave.png
where A will go to 63 and each output is its correct value from 30-35 and 0 elsewhere
The loop parameter you use to specify the number of 'tests' is A_tb'range, which happens to be 5 downto 0, or six tests, i is assigned 5,4,3,2,1 and 0 successively.
You want to specify i in 0 to 2**A-tb'length-1 or i in 0 to 63 to get all 64 possible A_tb 'binary' values.
(A_tb'length = 6, 2**6-1 = 63, where ** is the exponentiation operator, 2 to the 6th power minus 1 equals 63)
I found two syntax errors in your test bench, 20ns where the standard requires a space between 20 and ns:
constant period: time := 20 ns;
And entity ent where that should either be just ent (you have a component declaration ent) or entity work.ent and no need for a component declaration:
uut: ent port map(A=>A_tb, B=>B_tb);
or
uut: entity work.ent port map(A=>A_tb, B=>B_tb);
And in keeping with Russell's answer there is no implied logic replication in a loop other than through synthesis which unravels loop iterations by paralleling logic (the replication). Not all loop statements are intended as synthesis targets.
Test benches are generally not synthesized and are used to write tests (as in your case) for a VHDL model that might be used as a synthesis target.
First, loops are just fine, and common, in testbenches. #Russell's comment applies to RTL code. You can adapt his approach for this problem and make it work. You would need to use 64 as a sentinel (ending) value and do your end of test checks then. Keep in mind though that the most important thing you do is code for readability. Test cases generally run from top to bottom of a process one time.
You loop has some issues in addition to the recommendations #DavidKoontz gave. Specifically,
Your assertion is should not be checked when you expect B to be 0.
Using numeric_std_unsigned (requires VHDL-2008 compile switch) will simplify your conversions.
Keep an error count so you can report pass or failed at the end.
Keep your constants in the architecture or a package
So the modified code is:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.numeric_std_unsigned.all;
entity tb is
end tb;
architecture arch_tb of tb is
constant period: time := 20 ns;
...
begin
...
tb: process
variable ErrorCount :
begin
for i in i in 0 to 2**A-tb'length-1
A_tb <= to_slv(i,6);
wait for period;
if i >= 30 and i <= 35 then
if B_tb /= 3 then
ErrorCount := Error Count + 1 ;
report "B_tb = " & to_string(B_tb) & " Expecting: 0011" severity ERROR ;
end if;
else
if B_tb /= 0 then
ErrorCount := Error Count + 1 ;
report "B_tb = " & to_string(B_tb) & " Expecting: 0000" severity ERROR ;
end if;
end loop;
if ErrorCount = 0 then
report "Test Passed" severity NOTE ;
else
report "Test FAILED. There were " & to_string(ErrorCount) & " Errors " severity NOTE;
end if;
std.env.stop(0) ; -- testbench stops here
end process;
Note that the rules about using (or forbidding usage of) numeric_std_unsigned do not apply to testbenches.
You really should not be using a for loop for this. For loops in VHDL are used to REPLICATE LOGIC, not to do something some number of times. Try something like this in your test bench:
signal r_CLOCK : std_logic := '0';
signal r_INDEX : unsigned(5 downto 0) := (others => '0');
begin
r_CLOCK <= not r_CLOCK after period/2;
process (r_CLOCK)
begin
if rising_edge(r_CLOCK) then
r_INDEX <= r_INDEX + 1;
end if;
end process;
Now simply cast r_INDEX as std_logic_vector and pass it to your ent component.
This is Oracle 11.2g. In a PL/SQL function, I've got a loop whereby each iteration, I create a string and an integer associated with that string. The function returns the final concatenation of all the generated strings, sorted (depending on a function input parameter), either alphabetically or by the value of the integer. To give an idea, I'm generating something like this:
Iteration String Integer
1 Oslo 40
2 Berlin 74
3 Rome 25
4 Paris 10
If the input parameter says to sort alphabetically, the function output should look like this :
Berlin, Oslo, Paris, Rome
Otherwise, we return the concatenated strings sorted by the value of the associated integer:
Paris, Rome, Oslo, Berlin
What is the most appropriate data structure to achieve this sort? I've looked at collections, associative arrays and even varrays. I've been kind of shocked how difficult this seems to be to achieve in Oracle. I saw this question but it doesn't work in my case, as I need to be able to sort by both index and value: How to sort an associative array in PL/SQL? Is there a more appropriate data structure for this scenario, and how would you sort it?
Thanks!
It is very easy if you use PL/SQL as SQL and not like other languages. It is quite specific and sometimes is very nice exactly because of that.
Sometimes I really hate PL/SQL, but this case is absolutely about love.
See how easy it is:
create type it as object (
iter number,
stringval varchar2(100),
intval integer
);
create type t_it as table of it;
declare
t t_it := new t_it();
tmp1 varchar2(32767);
tmp2 varchar2(32767);
begin
t.extend(4);
t(1) := new it(1,'Oslo',40);
t(2) := new it(2,'Berlin',74);
t(3) := new it(3,'Rome',25);
t(4) := new it(4,'Paris',10);
select listagg(stringval,', ') within group (order by stringval),
listagg(stringval,', ') within group (order by intval)
into tmp1, tmp2
from table(t);
dbms_output.put_line(tmp1);
dbms_output.put_line(tmp2);
end;
/
drop type t_it;
drop type it;
Here you can see the problem that you must create global types, and this is what I hate it for. But they say in Oracle 12 it can be done with locally defined types so I am waiting for it :)
The output is:
Berlin, Oslo, Paris, Rome
Paris, Rome, Oslo, Berlin
EDIT
As far as you do not know the amount of iterations from the beginning the only way is to do extend on each iteration (this is only example of extending):
declare
iterator pls_integer := 1;
begin
/* some type of loop*/ loop
t.extend();
-- one way to assign
t(t.last) := new it(1,'Oslo',40);
-- another way is to use some integer iterator
t(iterator) := new it(1,'Oslo',40);
iterator := iterator + 1;
end loop;
end;
I prefer the second way because it is faster (does not calculate .last on each iteration).
This is an example of pure PL/SQL implementation that is based on the idea associative array (aka map or dictionary in other domains) is an ordered collection that is sorted by a key. That is a powerful feature that I have used multiple times. For input data structure in this example I decided to use a nested table of records (aka a list of records).
In this particular case however I'd probably go for similar implementation than in simon's answer.
create or replace package so36 is
-- input data structures
type rec_t is record (
iter number,
str varchar2(20),
int number
);
type rec_list_t is table of rec_t;
function to_str(p_list in rec_list_t, p_sort in varchar2 default 'S')
return varchar2;
end;
/
show errors
create or replace package body so36 is
function to_str(p_list in rec_list_t, p_sort in varchar2 default 'S')
return varchar2 is
v_sep constant varchar2(2) := ', ';
v_ret varchar2(32767);
begin
if p_sort = 'S' then
-- create associative array (map) v_map where key is rec_t.str
-- this means the records are sorted by rec_t.str
declare
type map_t is table of rec_t index by varchar2(20);
v_map map_t;
v_key varchar2(20);
begin
-- populate the map
for i in p_list.first .. p_list.last loop
v_map(p_list(i).str) := p_list(i);
end loop;
v_key := v_map.first;
-- generate output string
while v_key is not null loop
v_ret := v_ret || v_map(v_key).str || v_sep;
v_key := v_map.next(v_key);
end loop;
end;
elsif p_sort = 'I' then
-- this branch is identical except the associative array's key is
-- rec_t.int and thus the records are sorted by rec_t.int
declare
type map_t is table of rec_t index by pls_integer;
v_map map_t;
v_key pls_integer;
begin
for i in p_list.first .. p_list.last loop
v_map(p_list(i).int) := p_list(i);
end loop;
v_key := v_map.first;
while v_key is not null loop
v_ret := v_ret || v_map(v_key).str || v_sep;
v_key := v_map.next(v_key);
end loop;
end;
end if;
return rtrim(v_ret, v_sep);
end;
end;
/
show errors
declare
v_list so36.rec_list_t := so36.rec_list_t();
v_item so36.rec_t;
begin
v_item.iter := 1;
v_item.str := 'Oslo';
v_item.int := 40;
v_list.extend(1);
v_list(v_list.last) := v_item;
v_item.iter := 2;
v_item.str := 'Berlin';
v_item.int := 74;
v_list.extend(1);
v_list(v_list.last) := v_item;
v_item.iter := 3;
v_item.str := 'Rome';
v_item.int := 25;
v_list.extend(1);
v_list(v_list.last) := v_item;
v_item.iter := 4;
v_item.str := 'Paris';
v_item.int := 10;
v_list.extend(1);
v_list(v_list.last) := v_item;
dbms_output.put_line(so36.to_str(v_list));
dbms_output.put_line(so36.to_str(v_list, 'I'));
end;
/
show errors