Was shifting to a new apartment for the umpteenth time since I moved to Melbourne. While gathering my stuff I came across my Bachelor's files for VLSI design, Embedded stuff etc. My parents had shipped all that to US, and though it will be landing into recycling I decided to go through the programs once before parting ways with the files. Coincidently, they sent my friend Sonaki's VLSI file, some more happy memories from my Bachelor's.
I will be starting SV soon at work, and I decided to use the same undergrad simple kiddo programs to start with SV testbenches. So here it goes.
//----------------------------------------******************----------------------------
//8:1 mux, with enable
//-----------------------------------------****************-------------------------------
//Dataflow
//note: if you are using ncvhdl, the ports will be of type std_ulogic, and std_ulogic_vector.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity mux is
port( EN, S0, S1, S2: in STD_LOGIC;
I: in STD_LOGIC_VECTOR(7 downto 0);
Y: out STD_LOGIC );
end mux;
architecture dataflow of mux is
signal S0b, S1b, S2b, t0, t1, t2, t3, t4, t5, t6, t7, Y1, Y2: std_logic;
begin
S0b <= not S0;
S1b <= not S1;
S2b <= not S2;
t0<= I(0) AND S2b AND S1b AND S0b;
t1<= I(1) AND S2b AND S1b AND S0;
t2<= I(2) AND S2b AND S1 AND S0b;
t3<= I(3) AND S2b AND S1 AND S0;
t4<= I(4) AND S2 AND S1b AND S0b;
t5<= I(5) AND S2 AND S1b AND S0;
t6<= I(6) AND S2 AND S1 AND S0b;
t7<= I(7) AND S2 AND S1 AND S0;
Y1<= t0 OR t1 OR t2 OR t3;
Y2<= t4 OR t5 OR t6 OR t7;
Y <= (Y1 OR Y2) AND EN;
end dataflow;
//------------------*********------------
//Mux test bench - in verilog
//--------------------------------------------
`timescale 1ns/1ps
module test_mux(EN, bit3ctr, I, Y);
output reg EN; //DUT inputs reg
output reg [7:0] I;
input wire Y; //DUT outputs wire
output reg [2:0] bit3ctr;
mux DUT(
.EN (EN),
.S0 (bit3ctr[0]),
.S1 (bit3ctr[1]),
.S2 (bit3ctr[2]),
.I (I),
.Y (Y)
);
//assign S0 = bit3ctr[0]; //got error that A reg is not a legal lvalue in this context
initial begin
$monitor ("At t=%t, EN=%b, sel = %b, I = %b, Y = %b", $time, EN, bit3ctr, I, Y);
EN = 0;
bit3ctr = 3'b000;
I = 8'h38;
#55 EN = 1;
// while {} does not seem to work, took really long time but never went to next step!
// cannot use while (bit3ctr != 3'b000) begin #5 end because once u use begin, it expects a statement
// while (bit3ctr != 3'b000) #5 kind of acted like a if loop, when bit3ctr wasnt 0, it just went to next statement: I =AA
while (bit3ctr != 3'b000) begin I = 8'h83; #5; end
//zero delay stuff creates problems. If you dont add some delay between the 2 whiles, it never enters while 2
#5;
while (bit3ctr != 3'b000) begin I = 8'hAA; #5; end
#5;
while (bit3ctr != 3'b000) begin I = 8'h55; #5; end
#55 I = 8'hA5;
#55 I = 8'h5A;
#55
$finish;
end
always
begin
#5 bit3ctr = bit3ctr + 3'b001;
end
endmodule
//-----------------------Commands to run------------
ncvhdl mux.vhd
ncvlog mux_test.v
ncelab test_mux //test_mux is the name of testbench module
ncsim WORKLIB.test_mux:module
//-------I ran the testbench till 325 ns, some select Results to keep the blog short-----
At t= 0, EN=0, sel = 000, I = 00111000, Y = 0
At t= 55000, EN=1, sel = 011, I = 10000011, Y = 0
At t= 75000, EN=1, sel = 111, I = 10000011, Y = 1
At t= 105000, EN=1, sel = 101, I = 10101010, Y = 1
At t= 140000, EN=1, sel = 100, I = 01010101, Y = 1
//----------------Some other design architectures for mux----
architecture structural of mux is
component inv
port (A: in std_logic;
B: out std_logic);
end component;
component and5
port (A0, A1, A2, A3, A4: in std_logic;
Y: out std_logic);
end component;
component or8
port (A0, A1, A2, A3, A4, A5, A6, A7: in std_logic;
Y: out std_logic);
end component;
signal S0b, S1b, S2b, t0, t1, t2, t3, t4, t5, t6, t7, Y1, Y2: std_logic;
begin
U_I0: inv port map (S0, S0b);
U_I1: inv port map (S1, S1b);
U_I2: inv port map (S2, S2b);
U_A0: and5 port map (I(0), S2b, S1b, S0b, EN, t0);
U_A1: and5 port map (I(1), S2b, S1b, S0, EN, t1);
U_A2: and5 port map (I(2), S2b, S1, S0b, EN, t2);
U_A3: and5 port map (I(3), S2b, S1, S0, EN, t3);
U_A4: and5 port map (I(4), S2, S1b, S0b, EN, t4);
U_A5: and5 port map (I(5), S2, S1b, S0, EN, t5);
U_A6: and5 port map (I(6), S2, S1, S0b, EN, t6);
U_A7: and5 port map (I(7), S2, S1, S0, EN, t7);
U_O1: or8 port map (t0, t1, t2, t3, t4, t5, t6, t7, Y);
end structural;
now you need to add entity + architecture description for all components: inv, and5, or8
//---------------behav architecture of mux-------
//Note: S is a vector in behav, S: in STD_LOGIC_VECTOR (2 downto 0);
architecture behav of mux is
begin
process(S0, I, EN)
begin
if EN='0' then y<='0';
else
case S is
when "000" => y<=I(0);
when "001" => y<=I(1);
when "010" => y<=I(2);
when "011" => y<=I(3);
when "100" => y<=I(4);
when "101" => y<=I(5);
when "110" => y<=I(6);
when "111" => y<=I(7);
end case;
end if;
end process;
end behav;
//-------Comments on the testbench--------
// In this test bench user needs to check each output against Inputs and selects. I shifted next to //System Verilog because you can use "assert" and make your job much easier.
//-----------------------------*****************-----------------
//2:4 decoder
//-----------------------------*****************-----------------
//2:4 decoder combinational = dataflow
//Y(0) = S1b.S0b
//Y(1) = S1b.S0
//Y(2) = S1.S0b
//Y(3) = S1.S0
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity dec24 is
port( EN: in STD_uLOGIC;
S: in STD_uLOGIC_VECTOR(1 downto 0);
Y: out STD_uLOGIC_VECTOR(3 downto 0));
end dec24;
architecture behav of dec24 is
begin
process(S, EN)
begin
if EN ='1' then
case S is
when "00"=>y<="0001";
when "01"=>y<="0010";
when "10"=>y<="0100";
when "11"=>y<="1000";
when others=>y<="0000"
end case;
else
y<="0000";
end if;
end process;
end behav;
// structural architecture of 2:4 decoder
//UI1: inv port map (S0, S0b);
//UI2: inv port map (S1, S1b);
//UA0: and3 port map (S0b, S1b, EN, Y(0));
//UA1: and3 port map (S0, S1b, EN, Y(1));
//UA2: and3 port map (S0b, S1, EN, Y(2));
//UA3: and3 port map (S0, S1, EN, Y(3));
//-----------------------------*****************-----------------
//My 1st system verilog testbench: for 2:4 decoder
//-----------------------------*****************-----------------
`timescale 1ns/1ps
module test_dec24(EN, S, Y);
output reg EN; //DUT inputs reg
output reg [1:0] S;
input wire [3:0] Y; //DUT outputs wire
dec24 DUT(EN, S, Y);
initial begin
repeat(15)
begin
#2 S = $random ; EN = $random;
#3
if (EN==1'b0) assert (Y == 4'b0);
else
begin
if (S==2'b00) assert (Y== 4'b0001);
if (S==2'b01) assert (Y== 4'b0010);
if (S==2'b10) assert (Y== 4'b0100);
if (S==2'b11) assert (Y== 4'b1000);
end
$display("EN=%b, S=%b, Y=%b", EN, S, Y);
end
$finish;
end
endmodule
//---------commands to run---------
ncvhdl demux.vhd
ncvlog -sv dec24_test.sv
ncelab test_dec24
ncsim WORKLIB.test_dec24:module
//----select output, to keep the blog short
EN=1, S=00, Y=0001
EN=1, S=01, Y=0010
EN=1, S=10, Y=0100
EN=0, S=01, Y=0000
//-----------------------------*****************-----------------
//Full adder
//-----------------------------*****************-----------------
// Note: if you are using this code on FPGA, Sonaki wrote in her report that less macrocells were used up with dataflow architecture.
architecture dataflow of fadd is
signal c:std_logic_vector(3 downto 0);
begin
process(a, b,cin, c)
begin
s(0)<=a(0) xor b(0) xor cin;
c(0)<=(a(0) and b(0)) or (b(0) and cin) or (a(0) and cin);
s(1)<=a(1) xor b(1) xor c(0);
c(1)<=(a(1) and b(1)) or (b(1) and c(0)) or (a(1) and c(0));
s(2)<=a(2) xor b(2) xor c(1);
c(2)<=(a(2) and b(2)) or (b(2) and c(1)) or (a(2) and c(1));
s(3)<=a(3) xor b(3) xor c(2);
c(3)<=(a(3) and b(3)) or (b(3) and c(2)) or (a(3) and c(2));
cout<=c(3);
end process;
end dataflow;
// Side note: I wrote a SV testbench, and assigned values to a,b using $random. I forgot to assign a //value to cin, and look what happened :) :
//Assertion test_bit4fadd.__assert_1 has failed a=0100, b=0001, sum=001xx
//Assertion test_bit4fadd.__assert_1 has failed a=1001, b=0011, sum=0110x
//--------------Testbench that tests Adder + mux-------
// Output of adder is input of mux
`timescale 1ns/1ps
module test_fadd_mux(cin, a, b, sel, Y);
output reg cin; //DUT inputs reg
output reg [3:0] a, b;
output reg [1:0] sel;
input wire Y; //DUT outputs wire
//intermed
wire [4:0] SUM; //DUT outputs wire
bit4fadd DUT1(
.cin (cin),
.a (a),
.b (b),
.cout (SUM[4]),
.s (SUM[3:0])
);
mux DUT2(
.EN (1'b1),
.S0 (sel[0]),
.S1 (sel[1]),
.S2 (cin),
.I ({3'b0, SUM[4:0]}),
.Y (Y)
);
initial begin
repeat(15)
begin
a = $random ; b = $random; cin=$random; sel=$random;
#4
assert (SUM == a+b+cin);
//assert (Y==SUM[sel]);
if (cin==1'b0) assert (Y==SUM[sel]);
if (cin==1'b1 && sel==2'b00) assert (Y==SUM[4]);
if (cin==1'b1 && sel>2'b00) assert (Y==1'b0);
$display("a=%b, b=%b, cin=%b, sum=%b, sel[2]=%b, sel[1:0]=%b, mux_output Y=%b", a, b, cin, SUM, cin, sel, Y);
end
$finish;
end
endmodule
//--------commands to run----
ncvhdl 4bitadder.vhd
ncvhdl mux.vhd
ncvlog -sv fadd_mux_test.sv
ncelab test_fadd_mux
ncsim WORKLIB.test_fadd_mux:module
Parting thoughts:
Assertion based test will only check code + functionality. We still have a long way to go in verification. If we were to test, say an FSM with a assertion based test case, we are essentially only checking it at discrete time stamps. We need a better test case, to test the rtl at each instant of time.
Its called self checking test, I am yet to try my hand at that.
I will be starting SV soon at work, and I decided to use the same undergrad simple kiddo programs to start with SV testbenches. So here it goes.
//----------------------------------------******************----------------------------
//8:1 mux, with enable
//-----------------------------------------****************-------------------------------
//Dataflow
//note: if you are using ncvhdl, the ports will be of type std_ulogic, and std_ulogic_vector.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity mux is
port( EN, S0, S1, S2: in STD_LOGIC;
I: in STD_LOGIC_VECTOR(7 downto 0);
Y: out STD_LOGIC );
end mux;
architecture dataflow of mux is
signal S0b, S1b, S2b, t0, t1, t2, t3, t4, t5, t6, t7, Y1, Y2: std_logic;
begin
S0b <= not S0;
S1b <= not S1;
S2b <= not S2;
t0<= I(0) AND S2b AND S1b AND S0b;
t1<= I(1) AND S2b AND S1b AND S0;
t2<= I(2) AND S2b AND S1 AND S0b;
t3<= I(3) AND S2b AND S1 AND S0;
t4<= I(4) AND S2 AND S1b AND S0b;
t5<= I(5) AND S2 AND S1b AND S0;
t6<= I(6) AND S2 AND S1 AND S0b;
t7<= I(7) AND S2 AND S1 AND S0;
Y1<= t0 OR t1 OR t2 OR t3;
Y2<= t4 OR t5 OR t6 OR t7;
Y <= (Y1 OR Y2) AND EN;
end dataflow;
//------------------*********------------
//Mux test bench - in verilog
//--------------------------------------------
`timescale 1ns/1ps
module test_mux(EN, bit3ctr, I, Y);
output reg EN; //DUT inputs reg
output reg [7:0] I;
input wire Y; //DUT outputs wire
output reg [2:0] bit3ctr;
mux DUT(
.EN (EN),
.S0 (bit3ctr[0]),
.S1 (bit3ctr[1]),
.S2 (bit3ctr[2]),
.I (I),
.Y (Y)
);
//assign S0 = bit3ctr[0]; //got error that A reg is not a legal lvalue in this context
initial begin
$monitor ("At t=%t, EN=%b, sel = %b, I = %b, Y = %b", $time, EN, bit3ctr, I, Y);
EN = 0;
bit3ctr = 3'b000;
I = 8'h38;
#55 EN = 1;
// while {} does not seem to work, took really long time but never went to next step!
// cannot use while (bit3ctr != 3'b000) begin #5 end because once u use begin, it expects a statement
// while (bit3ctr != 3'b000) #5 kind of acted like a if loop, when bit3ctr wasnt 0, it just went to next statement: I =AA
while (bit3ctr != 3'b000) begin I = 8'h83; #5; end
//zero delay stuff creates problems. If you dont add some delay between the 2 whiles, it never enters while 2
#5;
while (bit3ctr != 3'b000) begin I = 8'hAA; #5; end
#5;
while (bit3ctr != 3'b000) begin I = 8'h55; #5; end
#55 I = 8'hA5;
#55 I = 8'h5A;
#55
$finish;
end
always
begin
#5 bit3ctr = bit3ctr + 3'b001;
end
endmodule
//-----------------------Commands to run------------
ncvhdl mux.vhd
ncvlog mux_test.v
ncelab test_mux //test_mux is the name of testbench module
ncsim WORKLIB.test_mux:module
//-------I ran the testbench till 325 ns, some select Results to keep the blog short-----
At t= 0, EN=0, sel = 000, I = 00111000, Y = 0
At t= 55000, EN=1, sel = 011, I = 10000011, Y = 0
At t= 75000, EN=1, sel = 111, I = 10000011, Y = 1
At t= 105000, EN=1, sel = 101, I = 10101010, Y = 1
At t= 140000, EN=1, sel = 100, I = 01010101, Y = 1
//----------------Some other design architectures for mux----
architecture structural of mux is
component inv
port (A: in std_logic;
B: out std_logic);
end component;
component and5
port (A0, A1, A2, A3, A4: in std_logic;
Y: out std_logic);
end component;
component or8
port (A0, A1, A2, A3, A4, A5, A6, A7: in std_logic;
Y: out std_logic);
end component;
signal S0b, S1b, S2b, t0, t1, t2, t3, t4, t5, t6, t7, Y1, Y2: std_logic;
begin
U_I0: inv port map (S0, S0b);
U_I1: inv port map (S1, S1b);
U_I2: inv port map (S2, S2b);
U_A0: and5 port map (I(0), S2b, S1b, S0b, EN, t0);
U_A1: and5 port map (I(1), S2b, S1b, S0, EN, t1);
U_A2: and5 port map (I(2), S2b, S1, S0b, EN, t2);
U_A3: and5 port map (I(3), S2b, S1, S0, EN, t3);
U_A4: and5 port map (I(4), S2, S1b, S0b, EN, t4);
U_A5: and5 port map (I(5), S2, S1b, S0, EN, t5);
U_A6: and5 port map (I(6), S2, S1, S0b, EN, t6);
U_A7: and5 port map (I(7), S2, S1, S0, EN, t7);
U_O1: or8 port map (t0, t1, t2, t3, t4, t5, t6, t7, Y);
end structural;
now you need to add entity + architecture description for all components: inv, and5, or8
//---------------behav architecture of mux-------
//Note: S is a vector in behav, S: in STD_LOGIC_VECTOR (2 downto 0);
architecture behav of mux is
begin
process(S0, I, EN)
begin
if EN='0' then y<='0';
else
case S is
when "000" => y<=I(0);
when "001" => y<=I(1);
when "010" => y<=I(2);
when "011" => y<=I(3);
when "100" => y<=I(4);
when "101" => y<=I(5);
when "110" => y<=I(6);
when "111" => y<=I(7);
end case;
end if;
end process;
end behav;
//-------Comments on the testbench--------
// In this test bench user needs to check each output against Inputs and selects. I shifted next to //System Verilog because you can use "assert" and make your job much easier.
//-----------------------------*****************-----------------
//2:4 decoder
//-----------------------------*****************-----------------
//2:4 decoder combinational = dataflow
//Y(0) = S1b.S0b
//Y(1) = S1b.S0
//Y(2) = S1.S0b
//Y(3) = S1.S0
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity dec24 is
port( EN: in STD_uLOGIC;
S: in STD_uLOGIC_VECTOR(1 downto 0);
Y: out STD_uLOGIC_VECTOR(3 downto 0));
end dec24;
architecture behav of dec24 is
begin
process(S, EN)
begin
if EN ='1' then
case S is
when "00"=>y<="0001";
when "01"=>y<="0010";
when "10"=>y<="0100";
when "11"=>y<="1000";
when others=>y<="0000"
end case;
else
y<="0000";
end if;
end process;
end behav;
// structural architecture of 2:4 decoder
//UI1: inv port map (S0, S0b);
//UI2: inv port map (S1, S1b);
//UA0: and3 port map (S0b, S1b, EN, Y(0));
//UA1: and3 port map (S0, S1b, EN, Y(1));
//UA2: and3 port map (S0b, S1, EN, Y(2));
//UA3: and3 port map (S0, S1, EN, Y(3));
//-----------------------------*****************-----------------
//My 1st system verilog testbench: for 2:4 decoder
//-----------------------------*****************-----------------
`timescale 1ns/1ps
module test_dec24(EN, S, Y);
output reg EN; //DUT inputs reg
output reg [1:0] S;
input wire [3:0] Y; //DUT outputs wire
dec24 DUT(EN, S, Y);
initial begin
repeat(15)
begin
#2 S = $random ; EN = $random;
#3
if (EN==1'b0) assert (Y == 4'b0);
else
begin
if (S==2'b00) assert (Y== 4'b0001);
if (S==2'b01) assert (Y== 4'b0010);
if (S==2'b10) assert (Y== 4'b0100);
if (S==2'b11) assert (Y== 4'b1000);
end
$display("EN=%b, S=%b, Y=%b", EN, S, Y);
end
$finish;
end
endmodule
//---------commands to run---------
ncvhdl demux.vhd
ncvlog -sv dec24_test.sv
ncelab test_dec24
ncsim WORKLIB.test_dec24:module
//----select output, to keep the blog short
EN=1, S=00, Y=0001
EN=1, S=01, Y=0010
EN=1, S=10, Y=0100
EN=0, S=01, Y=0000
//-----------------------------*****************-----------------
//Full adder
//-----------------------------*****************-----------------
// Note: if you are using this code on FPGA, Sonaki wrote in her report that less macrocells were used up with dataflow architecture.
architecture dataflow of fadd is
signal c:std_logic_vector(3 downto 0);
begin
process(a, b,cin, c)
begin
s(0)<=a(0) xor b(0) xor cin;
c(0)<=(a(0) and b(0)) or (b(0) and cin) or (a(0) and cin);
s(1)<=a(1) xor b(1) xor c(0);
c(1)<=(a(1) and b(1)) or (b(1) and c(0)) or (a(1) and c(0));
s(2)<=a(2) xor b(2) xor c(1);
c(2)<=(a(2) and b(2)) or (b(2) and c(1)) or (a(2) and c(1));
s(3)<=a(3) xor b(3) xor c(2);
c(3)<=(a(3) and b(3)) or (b(3) and c(2)) or (a(3) and c(2));
cout<=c(3);
end process;
end dataflow;
// Side note: I wrote a SV testbench, and assigned values to a,b using $random. I forgot to assign a //value to cin, and look what happened :) :
//Assertion test_bit4fadd.__assert_1 has failed a=0100, b=0001, sum=001xx
//Assertion test_bit4fadd.__assert_1 has failed a=1001, b=0011, sum=0110x
//--------------Testbench that tests Adder + mux-------
// Output of adder is input of mux
`timescale 1ns/1ps
module test_fadd_mux(cin, a, b, sel, Y);
output reg cin; //DUT inputs reg
output reg [3:0] a, b;
output reg [1:0] sel;
input wire Y; //DUT outputs wire
//intermed
wire [4:0] SUM; //DUT outputs wire
bit4fadd DUT1(
.cin (cin),
.a (a),
.b (b),
.cout (SUM[4]),
.s (SUM[3:0])
);
mux DUT2(
.EN (1'b1),
.S0 (sel[0]),
.S1 (sel[1]),
.S2 (cin),
.I ({3'b0, SUM[4:0]}),
.Y (Y)
);
initial begin
repeat(15)
begin
a = $random ; b = $random; cin=$random; sel=$random;
#4
assert (SUM == a+b+cin);
//assert (Y==SUM[sel]);
if (cin==1'b0) assert (Y==SUM[sel]);
if (cin==1'b1 && sel==2'b00) assert (Y==SUM[4]);
if (cin==1'b1 && sel>2'b00) assert (Y==1'b0);
$display("a=%b, b=%b, cin=%b, sum=%b, sel[2]=%b, sel[1:0]=%b, mux_output Y=%b", a, b, cin, SUM, cin, sel, Y);
end
$finish;
end
endmodule
//--------commands to run----
ncvhdl 4bitadder.vhd
ncvhdl mux.vhd
ncvlog -sv fadd_mux_test.sv
ncelab test_fadd_mux
ncsim WORKLIB.test_fadd_mux:module
Parting thoughts:
Assertion based test will only check code + functionality. We still have a long way to go in verification. If we were to test, say an FSM with a assertion based test case, we are essentially only checking it at discrete time stamps. We need a better test case, to test the rtl at each instant of time.
Its called self checking test, I am yet to try my hand at that.
No comments:
Post a Comment