Index a 2d array element - vhdl

In VHDL I have created the following package:
--! Custom, 8 bit register map package
package regmap_package is
--! Amount of registers in the array
constant reg_nr : natural := 8;
--! The 8bit register map array type
type regmap_t is array(0 to (reg_nr - 1)) of std_logic_vector(7 downto 0);
end package regmap_package;
Using this package one has access to a new type regmap_t, which is a 2d array of size reg_nr x 8 bits.
In vhdl, I cannot figure out how can I access a single bit in this array. I was able to access only single "registers", that is 8 whole bits. I am trying to get something like this:
some_signal <= regmap_var(0,1);
In order to access the 2nd (bit number 1, counting from 0) from the 1st register (nr 0).

This is not a 2D array, it is only a 1D array, where the elements are themselves a 1D array. Therefore the you need to keep each index into its own ()
some_sl_signal <= regmap_var(0)(1);

Related

VHDL: Generate a generic case statement with adjustable amount of cases

I want an approximation of the Tanh function by saving the values in a LUT (by this I am doing a quantization). I want to choose the Number of entries in the LUT.
As an not-correct example, I imagine a code like
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.fixed_pkg.all;
entity tanh_lut is
generic (
MIN_RANGE: real := 0.0; -- Minimum value of x
MAX_RANGE: real := 5.0; -- Maximum value of x
DATA_RANGE_int: positive:= 8;
DATA_RANGE_frac: positive:= 8;
);
Port ( DIN : in sfixed(DATA_RANGE_int-1 downto -(DATA_RANGE_frac-1));
DOUT : out sfixed(DATA_RANGE_int-1 downto -(DATA_RANGE_frac-1))
end tanh_lut;
architecture Behavioral of tanh_lut is
begin
lut_gen: for i in 0 to LUT_SIZE-1 generate
constant x_val : real := MIN_RANGE + (MAX_RANGE - MIN_RANGE) * i / (LUT_SIZE-1);
constant x_val_next : real := MIN_RANGE + (MAX_RANGE - MIN_RANGE) * (i+1) / (LUT_SIZE-1);
constant y_val : real := tanh(x_val);
if DIN>=x_val_previous AND DIN<x_val then
DOUT <= to_sfixed(tanh(y_val),DOUT ) ;
END IF
end generate;
end Behavioral;
Per example, if I want 4 entries in the range 0 to 3, I want that it is synthesizing a code like:
if DIN>0 AND DIN<=1 then
DOUT <= to_sfixed(0, DOUT);
else DIN>1 AND DIN<=2 then
DOUT <= to_sfixed(0.76159415595, DOUT);
else DIN>2 AND DIN<=3 then
DOUT <= to_sfixed(0.96402758007, DOUT);
else DIN>3 AND DIN<=4 then
DOUT <= to_sfixed(0.99505475368, DOUT);
End if
Is there any way that a code like this or a code which implements the idea behind this is possible?
A simple LUT with addresses is not possible because the addresses are always integer and DIN is fixed point, e.g., 1.5
The other possibility would be two LUTs, one for mapping the Input to an address, another for mapping the address to the LUT entry, e.g., LUT1: 1.5=> address 5, LUT2: address 5 => 0.90. But by this I would double the amount of resources what I dont want
My requirements: things like the tanh(x) should not be synthesized, only the final value of tanh(x). It shoudl also be hardware efficient
It does not matter if you use a nested „if-elsif“ construct or if you use a new „if“ construct for each check.
So you can create a loop like this:
for i in 0 to c_number_of_checks-1 loop
if c_boundaries(i)<DIN and DIN<=c_boundaries(i+1) then
DOUT <= c_output_values(i);
end if;
end loop;
Of course you must provide the constants c_number_of_checks and c_boundaries, c_output_values. This can be done by:
constant c_number_of_checks : natural := 4;
type array_of_your_data_type is array (natural range <>) of your_data_type;
constant c_boundaries : array_of_your_data_type(c_number_of_checks downto 0) := init_c_boundaries(c_number_of_checks);
constant c_output_values : array_of_your_data_type(c_number_of_checks-1 downto 0) := init_c_output_values(c_number_of_checks);
This means you will need the functions init_c_boundaries, init_c_output_values, which create arrays of values, which can initialize the constant c_boundaries and c_output_values.
But this is not complicated (you can use from ieee.math_real the function TANH), as the functions need not to be synthesizable, as they are called only during compile time.
As you see, you will have some effort. So perhaps it is easier to follow the other suggestions. If you do so (value as address of a LUT) you should think about automatic ROM inference, which is provided by several tool chains and will give you a very efficient (small) hardware.

Verilog code: initializing an 2D array using nested for loop

I'm recently trying to store a 2D array whose elements are consisted of 8-bit integers(0~4) by first input its elements row by row (treating it as an 1D array) and then access the values in the 1D array.
my procedure is as follow:
1.initialize an 2048-bits-1D (8*16*16) array (Row1 in the code) in test bench as input
2.cut the 1D array every 8 bits and assign the 8-bit number to the elements in the 2D array
3.use another 1D array (Row2 in the code) to observe the final result, because an array cannot be used as an instance output
So actually i'm turning an 1D array with 256 8-bit elements into a 2D array with 16*16 8-bit elements.
the problem is that after running the simulation,
it seems that most of the elements in the 2D array is in a high z state,
while the last of them have been assigned new value correctly.
Can anyone explain what's going on and how can i fix it?
To be clear, i put my verilog code below:
`timescale 1ns / 1ps
module convPE(
input clk,
input reset,
input [2048:1] Row1,
output [2048:1] Row2
);
wire [7:0] arr[17:0][17:0];
generate
genvar i,j;
for(i=16;i>=1;i=i-1)
begin:gen1
for(j=16;j>=1;j=j-1)
begin:gen2
assign arr[i][j]=Row1[(8*i*j) -: 8];
assign Row2[(8*i*j) -: 8]=arr[i][j];
end
end
end generate
endmodule
And here is the test bench :
`timescale 1ns / 1ps
module testbench;
// Inputs
reg [2048:1] Row1;
reg Clk;
reg Reset;
wire [2048:1] Row2;
convPE uut (
.clk(Clk),
.reset(Reset),
.Row1(Row1),
.Row2(Row2)
);
initial begin
// Initialize Inputs
Row1=2048'd0;
Row1[1784:1777]=8'd1;//1
Row1[1584:1577]=8'd1;
Row1[944:937]=8'd1;
Row1[376:369]=8'd1;
//2
Row1[1720:1713]=8'd2;
Row1[1600:1593]=8'd2;
Row1[1488:1481]=8'd2;
Row1[1480:1473]=8'd2;
Row1[1368:1361]=8'd2;
Row1[1344:1337]=8'd2;
Row1[1336:1329]=8'd2;
Row1[1120:1113]=8'd2;
Row1[1112:1105]=8'd2;
Row1[1080:1073]=8'd2;
Row1[1072:1065]=8'd2;
Row1[1056:1049]=8'd2;
Row1[984:977]=8'd2;
Row1[936:929]=8'd2;
Row1[856:849]=8'd2;
Row1[808:801]=8'd2;
Row1[728:721]=8'd2;
Row1[680:673]=8'd2;
Row1[608:601]=8'd2;
Row1[592:585]=8'd2;
Row1[584:577]=8'd2;
Row1[576:569]=8'd2;
Row1[568:561]=8'd2;
Row1[560:553]=8'd2;
Row1[544:537]=8'd2;
Row1[472:465]=8'd2;
Row1[424:417]=8'd2;
Row1[416:409]=8'd2;
//3
Row1[1712:1705]=8'd3;
Row1[1592:1585]=8'd3;
Row1[1472:1465]=8'd3;
Row1[1360:1353]=8'd3;
Row1[1352:1345]=8'd3;
Row1[1240:1233]=8'd3;
Row1[1208:1201]=8'd3;
Row1[1200:1193]=8'd3;
Row1[1064:1057]=8'd3;
Row1[992:985]=8'd3;
Row1[928:921]=8'd3;
Row1[864:857]=8'd3;
Row1[736:729]=8'd3;
Row1[600:593]=8'd3;
Row1[464:457]=8'd3;
Row1[456:449]=8'd3;
Row1[448:441]=8'd3;
Row1[440:433]=8'd3;
Row1[432:425]=8'd3;
//4
Row1[800:793]=8'd4;
Row1[672:665]=8'd4;
Row1[552:545]=8'd4;
#100
Reset=1'b1;
#100
Reset=1'b0;
Clk=1'b1;
// Add stimulus here
end
always
#50 Clk=~Clk;
endmodule
This (8*i*j) does not work. You have two nested loops so i in the second loop must increment in steps of 16. (The size of the inner loop) Try 8*(i*16+j)-1
Your code is somewhat inconsistent in that you sometimes use 0 and sometimes 1 as lowest index. I suggest you make all your arrays and vectors start from 0. [2047:0] It is the Verilog convention.
I have converted your code using the Verilog conventions I use. I also removed all superfluous signals like clock and reset. With the following code there are no X-es or Z-es in either Row2 or in arr.
`timescale 1ns / 1ps
module convPE(
input [2047:0] Row1,
output [2047:0] Row2
);
wire [7:0] arr[15:0][15:0];
generate
genvar i,j;
for(i=0; i<16; i=i+1)
begin:gen1
for(j=0; j<16; j=j+1)
begin:gen2
assign arr[i][j]=Row1[(8*(i*16+j)) +: 8];
assign Row2[(8*(i*16+j)) +: 8] =arr[i][j];
end
end
endgenerate
endmodule
`timescale 1ns / 1ps
module testbench;
// Inputs
reg [2047:0] Row1;
wire [2047:0] Row2;
convPE uut (
.Row1(Row1),
.Row2(Row2)
);
initial begin
#100; // I want to see X-es first
// Initialize Inputs
Row1=2048'd0;
#100;
$stop;
end
endmodule
The reason I use my method is because it is the standard way of mapping N-dimensional arrays of a certain type onto memory (which is linear) like e.g. C compilers do.
You can use 2048:1 but then you have to think much harder how to convert the indexes to a one-dimensional array. Probably replace the i and j in my formula with something like i-1,j-1.

VHDL pass range to procedure

I'm writing my own package to deal with generic matrix-like objects due to unavailability of VHDL-2008 (I'm only concerned with compilation and simulation for the time being).
My aim is getting a matrix M_out from a matrix M_in such that:
M_out(i downto 0, j downto 0) <= M_in(k+i downto k, l+j downto l);
using a subroutine of sort. For, let's say, semantic convenience and analogy with software programming languages my subroutine prototype should ideally look something like this:
type matrix is array(natural range <>, natural range <>) of std_logic;
...
procedure slice_matrix(signal m_out: out matrix;
constant rows: natural range<>;
constant cols: natural range<>;
signal m_in: in matrix);
The compiler does however regard this as an error:
** Error: custom_types.vhd(9): near "<>": syntax error
** Error: custom_types.vhd(9): near "<>": syntax error
Is it possible to pass a range as an argument in some way or shall I surrender and pass 4 separate indexes to calculate it locally?
An unconstrained index range natural range <> is not a VHDL object of class signal, variable, constant, or file. Thus it can not be passed into a subprogram. I wouldn't implement a slice operations as a procedure, because it's a function like behavior.
An implementation for working with matrices and slices thereof is provided by the PoC-Library. The implementation is provided in the vectors package.
function slm_slice(slm : T_SLM; RowIndex : natural; ColIndex : natural; Height : natural; Width : natural) return T_SLM is
variable Result : T_SLM(Height - 1 downto 0, Width - 1 downto 0) := (others => (others => '0'));
begin
for i in 0 to Height - 1 loop
for j in 0 to Width - 1 loop
Result(i, j) := slm(RowIndex + i, ColIndex + j);
end loop;
end loop;
return Result;
end function;
More specialized functions to slice off a row or column can be found in that file too. It also provides procedures to assign parts of a matrix.
This package works in simulation and synthesis.
Unfortunately, slicing multi dimensional arrays will not be part of VHDL-2017. I'll make sure it's discuss for VHDL-202x again.
Passing ranges into a subprogram will be allowed in VHDL-2017. The language change LCS 2016-099 adds this capability.

Signed multiplication result trim

What I have
I've two signed signals, 10b length one of them and 2b the other one.
signal R_S_R : signed(9 downto 0);
signal prbs_sup_u : signed(1 downto 0);
Then I want to multiply them like:
R_S_E <= R_S_R * prbs_sup_u;
Storing the result into another 10b signal.
Why 10b again
Because prbs_sup_u is 2b, and it'll only have [-1, 1] values (only those two). So, although result of multiplication is 12b, I think (only if I'm not mistaken) I should be able to store the posible results of the operation in another 10b signal.
So your question is...
After doing the multiplication, I should be able to dispose of two of the bits from the 12b result.
However, which ones? Since it's a signed signal, I don't know which one are disposable. Of course not the first one, since it's the sign, but after that...
Simply use the resize operation to truncate unrequired MSBs (magnitude) like:
R_S_E <= resize(R_S_R * prbs_sup_u, R_S_E'length);
You can find the documentation in numeric_std.resize:
-- Id: R.1
function RESIZE (ARG: SIGNED; NEW_SIZE: NATURAL) return SIGNED;
-- Result subtype: SIGNED(NEW_SIZE-1 downto 0)
-- Result: Resizes the SIGNED vector ARG to the specified size.
-- To create a larger vector, the new [leftmost] bit positions
-- are filled with the sign bit (ARG'LEFT). When truncating,
-- the sign bit is retained along with the rightmost part.
If the prbs_sup_u can only have value 1 or -1, then you can also consider:
if prbs_sup_u = 1 then
R_S_E <= R_S_R;
else -- prbs_sup_u = -1
R_S_E <= - R_S_R;
end if;
The operation may then be more obvious, and the circuit will be smaller, since the implementation does not have to include handling of the unused 0 and -2 values.

Unsigned logic, vector and addition - How?

I'm creating a program counter that is supposed to use only unsigned numbers.
I have 2 STD_LOGIC_VECTOR and a couple of STD_LOGIC. Is there anything I need to do so that they only use unsigned? At the moment I only have library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
I also need to increase one of the binary vectors by 1 under certain conditions (as you probably have guessed by now). Would you be so kind to explain how to perform such actions (using unsigned and adding up one) considering one of the vectors is output with 32 bits.
I'm guessing (I tried) Output <= Output + 1; won't do. Oh and I'm using a process.
In brief, you can add the ieee.numeric_std package to your architecture (library ieee; use ieee.numeric_std.all;) and then do the addition using:
Output <= std_logic_vector(unsigned(Output) + 1);
to convert your std_logic_vector to an unsigned vector, increment it, and finally convert the result back to an std_logic_vector.
Note that if Output is an output port, this won't work because you can't access the value of an output port within the same block. If that is the case, you need to add a new signal and then assign Output from that signal, outside your process.
If you do need to add a signal, it might be simpler to make that signal a different type than std_logic_vector. For example, you could use an integer or the unsigned type above. For example:
architecture foo of bar is
signal Output_int : integer range 0 to (2**Output'length)-1;
begin
PR: process(clk, resetn)
begin
if resetn='0' then
Output_int <= 0;
elsif clk'event and clk='1' then
Output_int <= Output_int + 1;
end if;
end process;
Output <= std_logic_vector(to_unsigned(Output_int, Output'length));
end foo;
Output_int is declared with a range of valid values so that tools will be able to determine both the size of the integer as well as the range of valid values for simulation.
In the declaration of Output_int, Output'length is the width of the Output vector (as an integer), and the "**" operator is used for exponentiation, so the expression means "all unsigned integers that can be expressed with as many bits as Output has".
For example, for an Output defined as std_logic_vector(31 downto 0), Output'length is 32. 232-1 is the highest value that can be expressed with an unsigned 32-bit integer. Thus, in the example case, the range 0 to (2**Output'length)-1 resolves to the range 0...4294967295 (232=4294967296), i.e. the full unsigned range that can be expressed with 32 bits.
Note that you'll need to add any wrapping logic manually: VHDL simulators will produce an error when you've reached the maximum value and try to increment by one, even if the synthesized logic will cleanly wrap around to 0.

Resources