Saturday, August 24, 2013

Bachelor's level coursework rtl codes

These are the vhdl labs from my BE E&TC course VLSI design. I was in the 4th year of engineering at that time. Having seen the undergrad coursework from my Master's university in the US, these seem too childish assignments. Its high time we revamp the course, and really really notch up the difficulty level of the course. Engineering colleges in India pop up like mushrooms, and the whole system is a disaster. Around 6 lakh students of my batch graduated from Engineering in Maharashtra alone!!

//-----------2 bit comparator------------
// This blog is not meant to spoon feed any juniors to a ready made code.
// Please write the truth table yourself, and that is your dataflow architecture for the comparator.
altb = a1b.b1 + a1b.a0b.b0 + b1.b0.a0b
aeqb = (a1 exnor b1).(a0 exnor b0)
agtb = a1.b1 + a1.a0.b0b + b0b.b1b.a0

architecture behav of 2bcomp is
begin
process (a,b)
begin
    if (a=b) then
        aeb<='1'; agb <='0'; alb<='0';
    elsif (a<b) then
        aeb<='0'; agb <='0'; alb<='1';

    elsif (a>b) then
        aeb<='0'; agb <='1'; alb<='0';

    else
        aeb<='0'; agb <='0'; alb<='0';

        end if;
end process;
end behav;

struct: define for exor, or3, inv, and3
declare as components, connect using port map


//------------delay ff-------------------//
clear input: highest priority

library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity DFF is
    port( D, clk, pre, clr: in STD_LOGIC;
          q, qb: out STD_LOGIC);
end DFF;

architecture DFF of DFF is
signal s: std_logic;
begin
    process(D, clk, pre, clr, s)
    begin
        if clr='0' then s<='0';
        else if pr='1' then s<='1';
             else if clk'event and clk='1' then s<=d;
                   end if;
             end if;
        end if;
    end process;
    q<=s;
    qb<=not s;
end DFF;

//-----T FF: T = 1 output toggles-------//

architecture TFF of TFF is
signal s: std_logic;
begin
    process(T, clk, pre, clr, s)
    begin
        if clr='0' then s<='0';
         elsif clk'event and clk='1' then
            if pre='1' then s<='1';
            elsif t='1' then s<= not s;
            else s<=s;
                end if;
        end if;
    end process;
    q<=s;
    qb<=not s;
end TFF;

Note: D-FF is already a component of FPGAs. 
T FF using D FF
clr =0, pre =0, clk = rising: T=0 q+ = q
                  T=1 q+ = qb
feedback D-FF q to input thru XOR

//----------shift resi--------------//
entity shift is
    port ( clk, clr, load, dir, bs: in std_logic;
        din: in STD_LOGIC_VECTOR(3 downto 0);
        dout: out STD_LOGIC_VECTOR(3 downto 0));
end shift;

architecture shift of shift is
signal s: STD_LOGIC_VECTOR(3 downto 0);
begin
    process(clk, clr, load, dir, bs, din, s)
    begin
        if clr='0' then s<="0000";
         elsif clk'event and clk='1' then
            if load='1' then s<=din;
            elsif bs='0' then
                if dir='1' then s<=s(2 downto 0) & '0';
                else s<='0' & s(3 downto 1);
                end if;
            else
                if dir='1' then s<=s(2 downto 0) & s(3);
                else s<=s(0) & s(3 downto 1);
                end if;
            end if;
        end if;
    end process;
    dout<=s;
end shift;


//--------up/down adder------------//
entity cntr is
    port (clk, rst, sel, load: in STD_LOGIC;
        din: in STD_LOGIC_VECTOR(3 downto 0);
        dout: out STD_LOGIC_VECTOR(3 downto 0));
end cntr;

architecture behave of cntr is
signal count: STD_LOGIC_VECTOR(3 downto 0);
signal clk1: std_logic;
signal count1: STD_LOGIC_VECTOR(19 downto 0);
begin
    process(clk1, rst)
    begin
        if(rst='0') then count<="0000";
        elsif (clk1='1' and clk1'event) then
            if load='0' then count<=din;
            elsif load='1' then count<=count;
            end if;
           
            if(sel='0') then count<=count+1;
            elsif(sel='1') then count<=count-1;
            end if;
        end if;
    end process;
    dout<=count;

    process(clk)
    begin
        if (clk='1' and clk'event) then
            count1<=count1+"0000_0000_0000_0000_001";
        end if;
        clk1<=count1(19);
    end process;
end behave;

//----------bidi buffer---------------------//
architecture bidi_buf of bidi_buf is
begin
    b<=a when (mode='1')
    else "zzzz";
    a<=b when (mode='0')
    else "zzzz";
end bidi_buf;

//-----------Traffic light controller FSM------------//
Description: green path 1: 20 sec, other 3 paths: red
yellow: 5 sec
green path 2: on 20 sec, other 3 red n so on


I need to code FSM and D-FF day in and day out at work. But my "highly accredited" Bachelor's engineering course never taught me to code FSM. :x 
My coursework took a shortcut approach to generated code from the tool using state diagram.

2 fsms in total: 1st is 8 state fsm outputs of which control the 12 lights
2nd is a clock gen
Reset -> S0 clk=0 -> S1 clk=0 -> S2 clk=0 -> S3 clk=0 ->
S4 clk=0 -> S5 clk=0 -> S6 clk=0 -> S7 clk=1 ->
S8 clk=1 -> S9 clk=0 -> S10 clk 1 -> S0

//------------FSM as generated by the tool-----------
entity traffic is
port (clk, rst: in std logic;
    light: out STD_LOGIC_VECTOR911 downto 0));
end traffic;

architecture arch of traffic is

signal clk2: STD_LOGIC;

type Sreg0_type is (S1, S2, S3, S4, S5, S6, S7, S8);
signal Sreg0: Sreg0_type;

FSM1: process (clk2, rst)
begin
    if rst='1' then Sreg0 <= S0;
    elsif clk2'event and clk2='1' then
        case Sreg0 is
            when S0 => Sreg0 <= S1;
            when S1 => Sreg0 <= S2;
            when S2 => Sreg0 <= S3;
            when S3 => Sreg0 <= S4;
            when S4 => Sreg0 <= S5;
            when S5 => Sreg0 <= S6;
            when S6 => Sreg0 <= S7;
            when S7 => Sreg0 <= S0;
            when others => null;
        end case;
    end if;
end process;

light <= "100_001_001_001" when (Sreg0 = S0) else
     "010_001_001_001" when (Sreg0 = S1) else
     "001_100_001_001" when (Sreg0 = S2) else
     "001_010_001_001" when (Sreg0 = S3) else
     "001_001_100_001" when (Sreg0 = S4) else
     "001_001_010_001" when (Sreg0 = S5) else
     "001_001_001_100" when (Sreg0 = S6) else
     "001_001_001_010" when (Sreg0 = S7) else


FSM2: process (clk, rst)
begin
    if rst='1' then Sreg1 <= SS0; clk2<= '0';
    elsif clk'event and clk='1' then
        case Sreg1 is
            when SS0 => Sreg1 <= SS1; clk2<= '0';
            when SS1 => Sreg1 <= SS2; clk2<= '0';
            when SS2 => Sreg1 <= SS3; clk2<= '0';
            when SS3 => Sreg1 <= SS4; clk2<= '0';
            when SS4 => Sreg1 <= SS5; clk2<= '0';
            when SS5 => Sreg1 <= SS6; clk2<= '0';
            when SS6 => Sreg1 <= SS7; clk2<= '0';
            when SS7 => Sreg1 <= SS8; clk2<= '0';
            when SS8 => Sreg1 <= SS9; clk2<= '1';
            when SS9 => Sreg1 <= SS10; clk2<= '0';
            when SS10 => Sreg1 <= SS0; clk2<= '1';
            when others => null;
        end case;
    end if;
end process;
end arch;


//--------------Static RAM----------
holds data till power is there, dynamic ram: needs refresh
ram contains address decoders, memory address array, out_en, rd/wrb

architecture behav of ram is
type vector_array is array (0 to 255) of std_logic_vector(7 downto 0);
signal memory: vector_array;
begin
    process(clk, rwb)
    begin
        if rwb='0' then
            if clk'event & clk='1' then
                memory(addr)<=data_in;
            end if;
        else
            if clk'event & clk='1' then
                data_out <= memory(addr);
            end if;
        end if;
    end process;
end behav;

synchronous read would allow you to use existing block RAM resources in FPGA for optimum device usage, improved timing.

Disclaimer: This is not even my code! I do not take any responsibility for its correctness. There are many better ways to code the same stuff esp the FSM. Please google for coursework from other Engineering colleges abroad/ use code examples from good authors like Frank Vahid etc.

SV testbench try 1

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.