I'm starting a big project in VHDL and I would like to have every basic components (adders, multiplexers, registers, ...) written such that they are as most ordered as possible.
I'm thinking to use different architectures for each entity (to go through different layer of abstraction or different kind of implementation) and then select one using configurations.
My question is: is it possible to recursively self-instantiate a component but with a different configuration?
For example, let's take the adder:
entity ADDER is
generic( ... );
port( ... );
end entity ADDER;
Then I would like to have different kind of architecture, for example:
-- Behavioral Add
architecture BHV of ADDER is
out <= A + B;
end architecture BHV;
-- Ripple Carry Adder
architecture RCA of ADDER is
...
end architecture RCA;
-- Carry Select Adder
architecture CSA of ADDER is
component ADDER -- <== this should be configured as RCA
...
end architecture CSA;
Is it possible to configure the adder used inside the Carry Select with a Ripple Carry without ending in an infinite instantiation loop?
Yes, it is possible to configure the adder used inside the Carry Select with a Ripple Carry without ending in an infinite instantiation loop. With recursive instantiation a termination condition is required - something that terminates the recursion. The configuration is carrying out that role.
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity ADDER is
generic( WIDTH : positive := 8 );
port( CIN : in std_logic;
A : in std_logic_vector(WIDTH-1 downto 0);
B : in std_logic_vector(WIDTH-1 downto 0);
F : out std_logic_vector(WIDTH-1 downto 0);
COUT : out std_logic);
end entity ADDER;
-- Ripple Carry Adder
architecture RCA of ADDER is
signal CIN0 : unsigned(0 downto 0);
signal FIN : unsigned(WIDTH downto 0);
begin
CIN0(0) <= CIN;
FIN <= resize(unsigned(A), WIDTH+1) + resize(unsigned(B), WIDTH+1) + CIN0; -- yes, I know it's not a ripple carry adder
F <= std_logic_vector(FIN(WIDTH-1 downto 0));
COUT <= FIN(WIDTH);
end architecture RCA;
-- Carry Select Adder
architecture CSA of ADDER is
component ADDER is
generic( WIDTH : positive );
port( CIN : in std_logic;
A : in std_logic_vector(WIDTH-1 downto 0);
B : in std_logic_vector(WIDTH-1 downto 0);
F : out std_logic_vector(WIDTH-1 downto 0);
COUT : out std_logic);
end component ADDER;
signal F0, F1 : std_logic_vector(WIDTH-1 downto 0);
signal COUT0, COUT1 : std_logic;
begin
ADD0: ADDER generic map( WIDTH => WIDTH)
port map (
CIN => '0' ,
A => A ,
B => B ,
F => F0 ,
COUT => COUT0 );
ADD1: ADDER generic map( WIDTH => WIDTH)
port map (
CIN => '1' ,
A => A ,
B => B ,
F => F1 ,
COUT => COUT1 );
COUT <= COUT1 when CIN = '1' else COUT0;
F <= F1 when CIN = '1' else F0;
end architecture CSA;
-- here's the configuration
configuration CSAC of ADDER is
for CSA
for all: ADDER
use entity work.ADDER(RCA);
end for;
end for;
end configuration CSAC;
http://www.edaplayground.com/x/2Yu3
Related
This is my first VHDL code, I have this multiplexer (two inputs, one selection bit) which has 8bit-vector inputs. How can I write a testing function that generates all possible vectors?
library IEEE;
use IEEE.std_logic_1164.all;
entity mux is
port(
in0, in1: in std_logic_vector(7 downto 0);
sel: in std_logic;
out0: out std_logic_vector(7 downto 0);
end mux;
architecture dataflow of mux is
begin
out0<=in1 when sel='1'
else in0;
end dataflow;
This is the testbench at the moment:
library IEEE;
use IEEE.std_logic_1164.all;
entity testbench is --empty
end testbench;
architecture tb of testbench is
-- DuT component
component mux is
port(
in0, in1: in std_logic_vector(7 downto 0);
sel: in std_logic;
out0: out std_logic);
end component;
signal tb_sel: std_logic;
signal tb_in0, tb_in1, tb_out0: std_logic_vector(7 downto 0);
begin
-- Connect DuT
DuT: mux port map(tb_in0, tb_in1, tb_sel, tb_out0);
process
begin
tb_sel <= 0;
tb_in0 <= "00000000";
tb_in1 <= "00000000";
-- TODO: test all possibilities
end process;
end tb;
Something like this can be used:
library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;
entity testbench is --empty
end testbench;
architecture tb of testbench is
signal tb_sel: std_logic;
signal tb_in0, tb_in1, tb_out0: std_logic_vector(7 downto 0);
begin
-- Connect DuT
DuT: entity work.mux port map(tb_in0, tb_in1, tb_sel, tb_out0);
process
begin
-- Done: Test all possibilities
for sel in 0 to 1 loop
for in0 in 0 to 2 ** tb_in0'length - 1 loop
for in1 in 0 to 2 ** tb_in1'length - 1 loop
-- Make stimuli
if sel = 0 then
tb_sel <= '0';
else
tb_sel <= '1';
end if;
tb_in0 <= std_logic_vector(to_unsigned(in0, tb_in0'length));
tb_in1 <= std_logic_vector(to_unsigned(in1, tb_in1'length));
-- Wait for output, also to ease viewing in waveforms
wait for 10 ns;
-- Test output
if sel = 0 then
assert tb_out0 = tb_in0 report "Wrong out0 output value for selected in0 input" severity error;
else
assert tb_out0 = tb_in1 report "Wrong out0 output value for selected in1 input" severity error;
end if;
end loop;
end loop;
end loop;
report "OK (not actual failure)" severity FAILURE;
wait;
end process;
end tb;
Note that I have used instantiation by entity for mux, to avoid the component declaration, where there actually was an error in the port list; clearly showing why it is a bad idea to write the same twice ;-)
Also not that I have included the IEEE numeric_std package.
It can surely be improved with respect to testing of X values also, but for a simple module like a mux the testing above will give the required coverage.
For more advanced testing, take a look at OSVVM.
The lines:
type some_array_type is array (0 to 4, 0 to 4) of unsigned(7 downto 0);
signal some_array : some_array_type := (others=>(others=>'0'));
cause vivado 2018.2 to throw the error:
[Synth 8-1807] character '0' is not in type unresolved_unsigned
for some reason in a VHDL 2008 file. What it the magical syntax to get Vivado to realize that I'm just trying to initialize the array to zeros? I shouldn't have to write a function to do this. I also tried unsigned((others=>(others=>'0')));
The code below can of course be ignored and isn't needed for anything at all. It is just there for the OCD people. "You have to always include a minimal working example!"
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity some_entity is
port (
clk, rst: in std_logic ;
);
end some_entity ;
architecture arch of some_entity is
type some_array_type is array (0 to 4, 0 to 4) of unsigned(7 downto 0);
-- throws error
signal some_array : some_array_type := (others=>(others=>'0'));
type some_other_array_type is array (natural range <>) of std_logic_vector(7 downto 0);
-- doesn't throw error
signal some_other_array : some_other_array_type(0 to 4) := (others=>(others=>'0'));
begin
-- some made up process
process(clk, rst)
begin
if(rising_edge(clk)) then
if rst = '1' then
some_array <= (others=>(others=>'0'));
else
some_array <= (others=>(others=>'1'));
end if;
end if;
end process;
end arch;
I'm translating a VHDL code to Verilog but I have a question in VHDL:
What is the use of the concatenation with the empty string in these lines?
Xp_m5b0 <= XX_m5(23 downto 0) & "";
Yp_m5b0 <= YY_m5(23 downto 0) & "";
It is said that it changes the type, but the types here are the same (std_logic_vector).
Here are the lines that showed the type:
entity IntMultiplier_LogicOnly_24_24_48_unsigned_F400_uid4 is
port ( clk, rst : in std_logic;
X : in std_logic_vector(23 downto 0);
Y : in std_logic_vector(23 downto 0);
R : out std_logic_vector(47 downto 0) );
end entity;
signal XX_m5 : std_logic_vector(23 downto 0);
signal YY_m5 : std_logic_vector(23 downto 0);
signal Xp_m5b0 : std_logic_vector(23 downto 0);
signal Yp_m5b0 : std_logic_vector(23 downto 0);
XX_m5 <= X ;
YY_m5 <= Y ;
In verilog after translation, this concatenation gives a compilation error:
assign Xp_m5b0 = {XX_m5[23:0], 0'b };
assign Yp_m5b0 = {YY_m5[23:0], 0'b };
So does it have a difference in the meaning if I removed it and made it like this:
assign Xp_m5b0 = XX_m5[23:0];
assign Yp_m5b0 = YY_m5[23:0];
"" is not an empty string, but an empty array. I haven't seen it used in this context, but it can be used to convert a literal to an array. I.e. consider the next code:
entity e is end entity;
library ieee;
architecture a of e is
use ieee.std_logic_1164.all;
signal a : std_logic_vector(0 downto 0);
signal b : std_logic;
begin
-- a <= b; -- fails
a <= b&""; -- works
end architecture;
But since XX_m5(23 downto 0) is already an array (slice), it should not be required here...
I want to use generate statement but in my code I have a case statements which only takes sequential statements.
Then I thought I will use it in a package where I can define a function such that there also I am getting error as : 'Illegal Sequential statement'.
So what can be done. Any suggestions?
Code block:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE work.my_package.all;
-- Entity for ALU component
-- Use this Entity for your C&A project
ENTITY ALU_E IS
PORT(
reset_n : in std_logic;
clk : in std_logic;
OperandA : in std_logic_vector(3 downto 0);
OperandB : in std_logic_vector(3 downto 0);
Operation : in std_logic_vector(2 downto 0);
Start : in std_logic;
Result_Low : out std_logic_vector(3 downto 0);
Result_High : out std_logic_vector(3 downto 0);
Ready : out std_logic;
Errorsig : out std_Logic);
END ALU_E;
architecture Behavioral_ALU of ALU_E is
signal c : std_logic_vector(7 downto 0);
signal carry_internal :std_logic_vector(4 downto 0);
COMPONENT fulladder IS
PORT(
a: IN std_logic;
b: IN std_logic;
cin : IN std_logic;
cout: OUT std_logic;
s: OUT std_logic );
END component fulladder;
begin
adders: for N in 0 to 3 generate
ff1:fulladder
port map
(a => OperandA(N),b => OperandB(N),cin => carry_internal(N),cout => carry_internal(N+1),s => c(N));
end generate adders;
c(4) <= carry_internal(4);
process(clk,reset_n)
begin
if reset_n = '0' then
if (clk'event) then
case Operation is
when "000" => --no operation
NULL;
when "001" => --Rotate left logical operator ?0000?&A by B steps
c <= rotlef (OperandA,OperandB);
when "010" => --Rotate right logical operator ?0000?&A by B steps (result width is 8 bit)
c <= rotrig (OperandA,OperandB);
when "011" => --Bitwise XOR operation
Result_Low <= OperandA xor OperandB;
when "100" => --Sum of A and B
--here i want to use a statement such that i can call the gatelevel --add function
--I have already all functions gatelevel defined when i try to use portmap or ----generate it gives an error illegal sequential statement
when Others =>
NULL;
end case;
end if;
end if;
end process;
end Behavioral_ALU;
There are a couple of misconceptions in the question.
First the title : there is no function in your actual code. What you have done with the for ... generate statement is generate a separate piece of hardware, operating in parallel with the main process. It will always operate, and always drive signal 'c' with the sum, as if you had written c <= a + b; in place of the for ... generate. It's always, continuously working, not a function you can call only when you want to.
Second, that means that both the adder and the clocked process drive c all the time, with different values. This will not go well... in fact you should see "XXXX" on signal c in simulation.
What I think you want to do is create a new signal, called sum for the adder's output, and drive sum, not c, in the for ... generate. Then, in the Case statement, assigning sum to c will accomplish the Add operation.
As far as why you get the error messages you do, a component instantiation statement or generate statement is a concurrent statement, while a case choice or a function body is comprised of sequential statements.
You don't need a function, you need the sum and carry out from the generate statement instantiated four fulladders.
o
Dummy up an entity/architecture pair for fulladder:
library ieee;
use ieee.std_logic_1164.all;
entity fulladder is
port (
a: in std_logic;
b: in std_logic;
cin: in std_logic;
cout: out std_logic;
s: out std_logic
);
end entity;
architecture foo of fulladder is
begin
s <= a xor b xor cin;
cout <= (a and b) or (a and cin) or (b and cin);
end architecture;
Dummy up a non functional my_package:
library ieee;
use ieee.std_logic_1164.all;
package my_package is
function rotlef (a, b: std_logic_vector) return std_logic_vector;
function rotrig (a, b: std_logic_vector) return std_logic_vector;
end package;
package body my_package is
function rotlef (a, b: std_logic_vector) return std_logic_vector is
variable ret_val: std_logic_vector (a'range);
begin
return ret_val;
end function;
function rotrig (a, b: std_logic_vector) return std_logic_vector is
variable ret_val: std_logic_vector (a'range);
begin
return ret_val;
end function;
end package body;
(note the function return value lengths match the left operand length)
Add a new declaration for the output of the generated fulladders:
architecture changed of alu_e is
signal c: std_logic_vector(7 downto 0);
signal carry_internal: std_logic_vector(4 downto 0);
signal s: std_logic_vector(3 downto 0); -- added
Change the generate statement to use the new signal for the sum:
adders:
for n in 0 to 3 generate
ff1:
fulladder
port map (
a => operanda(n),
b => operandb(n),
cin => carry_internal(n),
cout => carry_internal(n+1),
s => s(n) -- was c(n)
);
end generate;
-- c(4) <= carry_internal(4);
(eliminating the assignment to c(4))
And change the sequence of statements for choice "100":
when "100" => --sum of a and b
result_low <= s; -- added
result_high(0) <= carry_internal(4); --(un)signed?
And your design analyzes, elaborates and simulates (while not doing much - no assignments in the process for case others when operation is undriven and all Us, I didn't write a testbench to drive operation or provide it with a default value).
Notice the declaration of c has a length of 8 while the return value of the two functions will match their a input (length 4).
If you were to execute the dummy functions for operations "001" or "010" you'd get a simulation error due to length mismatch on the right hand side. I left this as is with no insight in to whether or not your rotlef or rotrig functions actually return a longer length.
In general you only want an 8 bit result for multiplies. The length of c and how it get's assigned to result_low and result_high aren't apparent (as yet).
It wasn't possible to discern whether you're doing signed or unsigned arithmetic without more detail. Instead of:
result_high(0) <= carry_internal(4); --(un)signed?
A signed sign extension could look like:
result_high <= (others => carry_internal(4)); --sign extended
Note that when you instantiate your multiplier you'd also want to use a new signal declaration for the 8 bit result. Modifying your rotate operations to assign result_low and result_high would allow c to be used for an instantiated multiply, although you might choose to rename it.
Think of the case statement in the process statement as instantiating a multiplexer, and in some cases you happen to be also expressing logic on inputs. The xor or the sign extension or function calls (which are expressions) are examples.
When you have instantiated components providing function you want to connect their output to a multiplexer input.
I have this code
--RAM module
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.numeric_std.all;
entity RAM is
generic(
address_length, data_length : integer);
port(
addr : in std_logic_vector(address_length-1 downto 0);
dat : inout std_logic_vector(data_length-1 downto 0);
rd, wr, en : in bit);
end entity RAM;
architecture RAM_impl of RAM is
type mem is array(2**address_length-1 downto 0) of std_logic_vector(data_length-1 downto 0);
begin
process(rd, wr, en)is
variable cont : mem;
begin
if(en = '1')then
if(wr = '1' and rd = '0')then
cont(to_integer(unsigned(addr))) := dat;
end if;
if(rd = '1' and wr = '0')then
dat <= cont(to_integer(unsigned(addr)));
end if;
end if;
end process;
end architecture RAM_impl;
--Test module
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.numeric_std.all;
entity Example4RAM is
end entity Example4RAM;
architecture Tester of Example4RAM is
signal rd, wr, en : bit;
signal str : std_logic_vector(15 downto 0);
signal ext : std_logic_vector(7 downto 0);
begin
module : entity work.RAM(RAM_impl)
generic map(
address_length => 16,
data_length => 8)
port map(str, ext, rd, wr, en);
tt : process is
begin
str <= X"0001";
ext <= "00000000";
rd <= '0'; wr <= '1';
wait for 5 ns;
en <= '1';
wait for 5 ns;
rd <= '0'; wr <= '0';
wait for 10 ns;
rd <= '1'; wr <= '0';
end process;
end architecture Tester;
When i run simulation on this RAM module str vector initializes fine but ext vector stays uninitialized. In RAM module str is in vector and ext is inout vector. Is this somehow making problem and does anyone know the solution? (I did change source since yesterday but it doesn't work still)
I added a RAM module and tinkered with the test stimulus slightly (ext is driven to all 'Z's when wr goes invalid (the behavioral model requires no hold over).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity RAM is
generic (
constant address_length: natural := 16;
constant data_length: natural := 8
);
port (
signal str: in std_logic_vector (address_length-1 downto 0);
signal ext: inout std_logic_vector (data_length-1 downto 0);
signal rd: in BIT;
signal wr: in BIT
);
end entity;
architecture RAM_impl of RAM is
type ram_array is array (natural range address_length-1 downto 0)
of std_logic_vector (data_length-1 downto 0);
signal mem_array: ram_array;
begin
MEMORY:
process (str, ext, rd, wr)
variable addr: natural range 0 to 2**address_length -1 ;
begin
addr := TO_INTEGER(UNSIGNED(str)); -- heed the warnings
if wr = '1' then
mem_array(addr) <= ext;
end if;
if rd = '0' then
ext <= (others => 'Z');
else
ext <= mem_array(addr);
end if;
end process;
end architecture;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- use IEEE.numeric_std.ALL;
entity Example4RAM is
end entity Example4RAM;
architecture Tester of Example4RAM is
signal rd,wr,clk: bit;
signal str: std_logic_vector(15 downto 0);
signal ext: std_logic_vector(7 downto 0);
begin
module:
entity work.RAM(RAM_impl)
generic map (
address_length=>16,
data_length=>8
)
port map (
str,
ext,
rd,
wr
)
;
tt:
process
begin
str<=X"0001";
ext<="00000000";
wait for 5 ns;
rd<='0';wr<='1';
wait for 5 ns;
rd<='0';wr<='0';
ext <= (others => 'Z'); -- ADDED
wait for 10 ns;
rd<='1';wr<='0';
wait for 20 ns; -- ADDED
str <=X"0002"; -- ADDED
wait for 20 ns; -- ADDED
wait;
end process;
end architecture Tester;
The change to the stimulus includes a change to the RAM address showing that reading an uninitialized location returns 'U's (uu on the waveform):
ghdl -a exampleram.vhdl
ghdl -r Example4RAM --wave=Example4RAM.ghw
../../../../libraries/ieee/numeric_std-body.v93:2098:7:#0ms:(assertion warning):
NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0
open *.ghw
Essentially, the process and the RAM drive ext with all 'Z's whenever either one shouldn't be driving a value out. Writing before reading hides the 'U' values from str address X"0001". As you see, if the address is changed to a location that is not initialized, the 'U's show up. Resolution delivers the RAM read data or provides write data to the RAM array on the bidirectional data bus (ext).
(This was done on a Mac with a ghdl mcode version (direct compile, like for Windows, requiring no explicit elaboration), and displayed using GTKWave).
The assertion warning (metavalue detected) comes from the default value assigned to str (all 'U's) at time zero (#0ms).