add provided RTL models

This commit is contained in:
Arthur Lu 2022-08-11 22:01:28 -07:00
parent 652ecf2686
commit 31b077c5f2
12 changed files with 674 additions and 0 deletions

55
RTL/ALU.sv Normal file
View File

@ -0,0 +1,55 @@
// Create Date: 2018.10.15
// Module Name: ALU
// Project Name: CSE141L
//
// Revision 2022.04.30
// Additional Comments:
// combinational (unclocked) ALU
import definitions::*; // includes package "definitions"
module ALU #(parameter W=8)(
input [W-1:0] InputA, // data inputs
InputB,
input SC_in, // shift or carry in
input [ 2:0] OP, // ALU opcode, part of microcode
output logic [W-1:0] Out, // or: output reg [7:0] OUT,
output logic PF, // reduction parity
output logic Zero, // output = zero flag
output logic SC_out // shift or carry out
// you may provide additional status flags as inputs, if desired
);
op_mne op_mnemonic; // type enum: used for convenient waveform viewing
// InputA = current LFSR state
// InputB = tap_pattern
always_comb begin
Out = 0;
SC_out = 0; // No Op = default
case(OP)
kADD : {SC_out,Out} = {1'b0,InputA} + InputB; // add
kLSH : {SC_out,Out} = {InputA[7:0],SC_in}; // shift left, fill in with SC_in
// for logical left shift, tie SC_in = 0
kRSH : {Out,SC_out} = {SC_in, InputA[7:0]}; // shift right
kXOR : Out = InputA ^ InputB; // exclusive OR
kAND : Out = InputA & InputB; // bitwise AND
endcase
end
always_comb // assign Zero = !Out;
case(Out)
'b0 : Zero = 1'b1;
default : Zero = 1'b0;
endcase
always_comb
PF = ^Out; // Out[7]^Out[6]^...^Out[0] // reduction XOR
always_comb
op_mnemonic = op_mne'(OP); // displays operation name in waveform viewer
endmodule
/* InputA=10101010 SC_in = 1
kLSH Out = 01010101
*/

56
RTL/Ctrl.sv Normal file
View File

@ -0,0 +1,56 @@
// CSE141L
import definitions::*;
// control decoder (combinational, not clocked)
// inputs from instrROM, ALU flags
// outputs to program_counter (fetch unit)
module Ctrl (
input[ 8:0] Instruction, // machine code
input[ 7:0] DatMemAddr,
output logic Jump ,
BranchEn ,
RegWrEn , // write to reg_file (common)
MemWrEn , // write to mem (store only)
LoadInst , // mem or ALU to reg_file ?
PCTarg ,
tapSel ,
Ack // "done w/ program"
// output logic[2:0] ALU_inst
);
/* ***** All numerical values are completely arbitrary and for illustration only *****
*/
// ALU commands
//assign ALU_inst = Instruction[2:0];
// STR commands only -- write to data_memory
assign MemWrEn = Instruction[8:6]==3'b110;
// all but STR and NOOP (or maybe CMP or TST) -- write to reg_file
assign RegWrEn = Instruction[8:7]!=2'b11;
// route data memory --> reg_file for loads
// whenever instruction = 9'b110??????;
assign LoadInst = Instruction[8:6]==3'b110; // calls out load specially
assign tapSel = LoadInst && DatMemAddr=='d62;
// jump enable command to program counter / instruction fetch module on right shift command
// equiv to simply: assign Jump = Instruction[2:0] == kRSH;
always_comb
if(Instruction[2:0] == kRSH)
Jump = 1;
else
Jump = 0;
// branch every time instruction = 9'b?????1111;
assign BranchEn = &Instruction[3:0];
// whenever branch or jump is taken, PC gets updated or incremented from "Target"
// PCTarg = 2-bit address pointer into Target LUT (PCTarg in --> Target out
assign PCTarg = Instruction[3:2];
// reserve instruction = 9'b111111111; for Ack
assign Ack = &Instruction;
endmodule

39
RTL/DataMem.sv Normal file
View File

@ -0,0 +1,39 @@
// Create Date: 2017.01.25
// Revision: 2022.05.04 made data width parametric
// Design Name:
// Module Name: DataMem
// single address pointer for both read and write
// CSE141L
module DataMem #(parameter W=8, D=8)(
input Clk,
Reset, // again, note use of Reset for preloads
WriteEn,
input [D-1:0] DataAddress, // 8-bit-wide pointer to 256-deep memory
input [W-1:0] DataIn, // 8-bit-wide data path, also
output logic[W-1:0] DataOut);
logic [W-1:0] Core[2**D]; // 8x256 two-dimensional array -- the memory itself
always_comb // reads are combinational
DataOut = Core[DataAddress];
/* optional way to plant constants into DataMem at startup
initial
$readmemh("dataram_init.list", Core);
*/
always_ff @ (posedge Clk) // writes are sequential
/*( Reset response is needed only for initialization (see inital $readmemh above for another choice)
if you do not need to preload your data memory with any constants, you may omit the if(Reset) and the else,
and go straight to if(WriteEn) ...
*/
if(Reset) begin
// you may initialize your memory w/ constants, if you wish
for(int i=128;i<256;i++)
Core[ i] <= 0;
Core[ 16] <= 254; // overrides the 0 ***sample only***
Core[244] <= 5; // likewise
end
else if(WriteEn)
Core[DataAddress] <= DataIn;
endmodule

24
RTL/Definitions.sv Normal file
View File

@ -0,0 +1,24 @@
//This file defines the parameters used in the alu
// CSE141L
// Rev. 2020.5.27
// import package into each module that needs it
// packages very useful for declaring global variables
package definitions;
/* Instruction map
const logic [2:0]kADD = 3'b000;
const logic [2:0]kLSH = 3'b001;
const logic [2:0]kRSH = 3'b010;
const logic [2:0]kXOR = 3'b011;
const logic [2:0]kAND = 3'b100;
const logic [2:0]kSUB = 3'b101;
const logic [2:0]kCLR = 3'b110;
*/
// enum names will appear in timing diagram
typedef enum logic[2:0] {
ADD, LSH, BSH, XOR,
AND, SUB, CLR } op_mne;
// note: kADD is of type logic[2:0] (3-bit binary)
// ADD is of type enum -- equiv., but watch casting
// see ALU.sv for how to handle this
endpackage // definitions

36
RTL/InstFetch.sv Normal file
View File

@ -0,0 +1,36 @@
// Design Name: basic_proc
// Module Name: InstFetch
// Project Name: CSE141L
// Description: instruction fetch (pgm ctr) for processor
//
// Revision: 2019.01.27
// Suggested ProgCtr width 10 t0 12 bits
module InstFetch #(parameter T=10)( // PC width -- up to 32, if you like
input Reset, // reset, init, etc. -- force PC to 0
Start, // begin next program in series (request issued by test bench)
Clk, // PC can change on pos. edges only
BranchAbs, // jump unconditionally to Target value
BranchRelEn, // jump conditionally to Target + PC
ALU_flag, // flag from ALU, e.g. Zero, Carry, Overflow, Negative (from ARM)
input [T-1:0] Target, // jump ... "how high?"
output logic [T-1:0] ProgCtr // the program counter register itself
);
// you may wish to use either absolute or relative branching
// you may use both, but you will need appropriate control bits
// branch/jump is how we handle gosub and return to main
// program counter can clear to 0, increment, or jump
always_ff @(posedge Clk) // or just always; always_ff is a linting construct
if(Reset)
ProgCtr <= 0; // for first program; want different value for 2nd or 3rd
else if(Start) // hold while start asserted; commence when released
ProgCtr <= 0; //or <= ProgCtr; holds at starting value
else if(BranchAbs) // unconditional absolute jump
ProgCtr <= Target; // how would you make it conditional and/or relative?
else if(BranchRelEn && ALU_flag) // conditional relative jump
ProgCtr <= Target + ProgCtr; // how would you make it unconditional and/or absolute
else
ProgCtr <= ProgCtr+'b1; // default increment (no need for ARM/MIPS +4 -- why?)
endmodule

32
RTL/InstFetch_TB.sv Normal file
View File

@ -0,0 +1,32 @@
module InstFetch_TB();
logic Reset = 1'b1,
Start = 1'b0,
Clk = 1'b0,
BranchAbs = 1'b0,
BranchRelEn = 1'b0,
ALU_flag = 1'b0;
logic [9:0] Target = 'b1; //10'h3fc;//'1; -4
wire [9:0] ProgCtr;
InstFetch IF1(.*);
always begin
#5ns Clk = 1'b1;
#5ns Clk = 1'b0;
end
initial begin
#20ns Reset = 1'b0;
#20ns Start = 1'b1;
#20ns Start = 1'b0;
#120ns BranchAbs = 1'b1; // should reset PC to 0
#30ns BranchAbs = 1'b0;
#250ns BranchRelEn = 1'b1;
Target = 'h3fc; // -4
#20ns ALU_flag = 1'b1;
#40ns ALU_flag = 1'b0;
#240ns $stop;
end
endmodule

52
RTL/InstROM.sv Normal file
View File

@ -0,0 +1,52 @@
// Create Date: 15:50:22 10/02/2019
// Design Name:
// Module Name: InstROM
// Project Name: CSE141L
// Tool versions:
// Description: Verilog module -- instruction ROM template
// preprogrammed with instruction values (see case statement)
//
// Revision: 2020.08.08
//
module InstROM #(parameter A=12, W=9) (
input [A-1:0] InstAddress,
output logic[W-1:0] InstOut);
// (usually recommended) expression
// need $readmemh or $readmemb to initialize all of the elements
// declare 2-dimensional array, W bits wide, 2**A words deep
logic[W-1:0] inst_rom[2**A];
always_comb InstOut = inst_rom[InstAddress];
initial begin // load from external text file
$readmemb("machine_code.txt",inst_rom);
end
// Sample instruction format:
// {3bit opcode, 3bit rs or rt, 3bit rt, immediate, or branch target}
// then use LUT to map 3 bits to 10 for branch target, 8 for immediate
/* alternative to code shown below, which may be simpler -- either is fine
always_comb begin
InstOut = 'b0000000000; // default
case (InstAddress)
//opcode = 0 lhw, rs = 0, rt = 1
0 : InstOut = 'b0000000001; // load from address at reg 0 to reg 1
// opcode = 1 addi, rs/rt = 1, immediate = 1
1 : InstOut = 'b0001001001; // addi reg 1 and 1
// opcode = 2 shw, rs = 0, rt = 1
2 : InstOut = 'b0010000001; // sw reg 1 to address in reg 0
// opcode = 3 beqz, rs = 1, target = 1
3 : InstOut = 'b0011001001; // beqz reg1 to absolute address 1
// opcode = 15 halt
4 : InstOut = '1; // equiv to 10'b1111111111 or 'b1111111111 halt
// (default case already covered by opening statement)
endcase
end
*/
endmodule

46
RTL/RegFile.sv Normal file
View File

@ -0,0 +1,46 @@
// Create Date: 2019.01.25
// Design Name: CSE141L
// Module Name: reg_file
// Revision: 2022.05.04
// Additional Comments: allows preloading with user constants
// This version is fully synthesizable and highly recommended.
/* parameters are compile time directives
this can be an any-width, any-depth reg_file: just override the params!
*/
module RegFile #(parameter W=8, D=4)( // W = data path width (leave at 8); D = address pointer width
input Clk,
Reset, // note use of Reset port
WriteEn,
input [D-1:0] RaddrA, // address pointers
RaddrB,
Waddr,
input [W-1:0] DataIn,
output [W-1:0] DataOutA, // showing two different ways to handle DataOutX, for
output logic [W-1:0] DataOutB // pedagogic reasons only
);
// W bits wide [W-1:0] and 2**4 registers deep
logic [W-1:0] Registers[2**D]; // or just registers[16] if we know D=4 always
// combinational reads
/* can use always_comb in place of assign
difference: assign is limited to one line of code, so
always_comb is much more versatile
*/
assign DataOutA = Registers[RaddrA]; // assign & always_comb do the same thing here
always_comb DataOutB = Registers[RaddrB]; // can read from addr 0, just like ARM
// sequential (clocked) writes
always_ff @ (posedge Clk)
if (Reset) begin
for(int i=0; i<2**D; i++)
Registers[i] <= 'h0;
// we can override this universal clear command with desired initialization values
Registers[0] <= 'd30; // loads 30 (=0x1E) into RegFile address 0
Registers[2] <= 'b101; // loads 00000101 into RegFile address 2
end
else if (WriteEn) // works just like data_memory writes
Registers[Waddr] <= DataIn;
endmodule

151
RTL/TopLevel.sv Normal file
View File

@ -0,0 +1,151 @@
// Revision Date: 2022.05.05
// Design Name: BasicProcessor
// Module Name: TopLevel
// CSE141L -- partial only
module TopLevel( // you will have the same 3 ports
input Reset, // init/reset, active high
Start, // start next program
Clk, // clock -- posedge used inside design
output logic Ack // done flag from DUT
);
// program counter / instructon fetch connections
wire [ 1:0] TargSel; // for branch LUT select
wire [ 9:0] PgmCtr, // program counter (wider if you wish)
PCTarg;
wire [ 8:0] Instruction; // our 9-bit opcode
// data path connections -- everything is 8 bits wide
wire [ 7:0] ReadA, ReadB; // reg_file outputs
wire [ 7:0] InA, InB, // ALU operand inputs
ALU_out; // ALU result
wire [ 7:0] RegWriteValue, // data in to reg file
MemWriteValue, // data in to data_memory
MemReadValue, // data out from data_memory
DatMemAddr , //
Immediate; //
// control wires
wire Imm, // inserts Immediate into ALU
MemWrite, // data_memory write enable
RegWrEn, // reg_file write enable
Zero, // ALU output = 0 flag
Jump, // to program counter: jump
BranchEn; // to program counter: branch enable
// ALU status register, analogous to ARM
logic PFq, // delayed/stored parity bit from ALU
SCq, // delayed/stored shift-carry out flag
Zq; // delayed/stored null out flag from ALU
logic[15:0] CycleCt; // standalone; NOT PC!
// Fetch stage = Program Counter + Instruction ROM
// "InstFetch" = PC register + branch/increment logic
InstFetch IF1 ( // this is the program counter module
.Reset (Reset ) , // reset to 0
.Start (Start ) , // SystemVerilog shorthand for .grape(grape) is just .grape
.Clk (Clk ) , // here, (Clk) is required in Verilog, optional in SystemVerilog
.BranchAbs (Jump ) , // jump enable
.BranchRelEn (BranchEn) , // branch enable
.ALU_flag (Zero ) , //
.Target (PCTarg ) , // "where to?" or "how far?" during a jump or branch
.ProgCtr (PgmCtr ) // program count = index to instruction memory
);
LUT LUT1(.Addr (TargSel ) ,
.Target (PCTarg )
);
// instruction ROM -- holds the machine code selected by program counter
// don't change W(9); increase A(10) if your machine code exceeds 1K lines
InstROM #(.W(9),.A(10)) IR1(
.InstAddress (PgmCtr ) ,
.InstOut (Instruction)
);
// Decode stage = Control Decoder + Reg_file
// Control decoder
Ctrl Ctrl1 (
.Instruction (Instruction) , // from instr_ROM
// outputs
.Jump (Jump ) , // to PC to handle jump/branch instructions
.BranchEn (BranchEn ) , // to PC
.RegWrEn (RegWrEn ) , // register file write enable
.MemWrEn (MemWrite ) , // data memory write enable
.LoadInst (LoadInst ) , // selects memory vs ALU output as data input to reg_file
.PCTarg (TargSel ) ,
.tapSel (tapSel ) ,
.DatMemAddr (DatMemAddr ) ,
.Ack (Ack ) // "done" flag
);
// reg file -- don't change W(8); may increase to D(4)
// I arbitrarily mapped to Instructon fields [5:3] and [2:0]
// you do not have to do this!!!
RegFile #(.W(8),.D(3)) RF1 ( // D(3) makes this 8 elements deep
.Clk ,
.WriteEn (RegWrEn) ,
.RaddrA (Instruction[5:3]), // see hint below
.RaddrB (Instruction[2:0]), // or perhaps (Instruction[2:0]+1);
// by choosing Waddr = RaddrA, I am doing write-in-place operations
// such as A = A+B, as opposed to C = A+B
.Waddr (Instruction[5:3]), //3'b0 // mux above
.DataIn (RegWriteValue) ,
.DataOutA (ReadA ) ,
.DataOutB (ReadB )
);
// a trick you MAY find useful:
// use 2 instructon bits to select 2 adjacent addresses in RegFile
// .RaddA ({Instruction[1:0],1'b0}),
// .RaddB ({Instruction[1:0],1'b1}),
// logic[3:0] Tap_ptr; // tap pattern selector
// sample LUT from a different program
logic[7:0] Immediate; // tap pattern itself
Immediate_LUT IL1(.addr(Instruction[5:3]), .datOut(Immediate));
assign InA = Imm? Immediate : ReadA; // connect RF out to ALU in
assign InB = ReadB; // interject switch/mux if needed/desired
assign MemWriteValue = ReadB;
// controlled by Ctrl1 -- must be high for load from data_mem; otherwise usually low
assign RegWriteValue = LoadInst? MemReadValue : ALU_out; // 2:1 switch into reg_file
ALU ALU1 (
.InputA (InA), //(ReadA),
.InputB (InB),
.SC_in (PFq), // registered version of output flag
.OP (Instruction[8:6]),
.Out (ALU_out),//regWriteValue),
.PF (PF), // output parity status flag
.Zero (Zero), // null output status flag
.SC_out (SCout), // shift/carry output flag
); // other flags as desired?
// equiv. to ARM ALU status register
// store flags for next clock cycle
always @(posedge Clk) begin
SCq <= SCout;
PFq <= PF;
Zq <= Zero;
end
DataMem DM1(
.DataAddress (ReadA) ,
.WriteEn (MemWrite),
.DataIn (MemWriteValue),
.DataOut (MemReadValue) ,
.Clk (Clk) ,
.Reset (Reset)
);
//always_comb chosen_bit = MemReadValue[5:2];
/* count number of instructions executed
not part of main design, potentially useful
This one halts when Ack is high
*/
always_ff @(posedge Clk)
if (Reset == 1) // if(start)
CycleCt <= 0;
else if(Ack == 0) // if(!halt)
CycleCt <= CycleCt+16'b1;
endmodule

78
RTL/TopLevelLab2.sv Normal file
View File

@ -0,0 +1,78 @@
// team name quarter
module TopLevel( // you will have the same 3 ports
input Reset, // init/reset, active high
Start, // start next program
Clk, // clock -- posedge used inside design
output logic Ack // done flag from DUT
);
InstFetch IF1 ( // this is the program counter module
.Reset (Reset ) , // reset to 0
.Start (Start ) , // SystemVerilog shorthand for .grape(grape) is just .grape
.Clk (Clk ) , // here, (Clk) is required in Verilog, optional in SystemVerilog
.BranchAbs (Jump ) , // jump enable
.BranchRelEn (BranchEn) , // branch enable
.ALU_flag (Zero ) , //
.Target (PCTarg ) , // "where to?" or "how far?" during a jump or branch
.ProgCtr (PgmCtr ) // program count = index to instruction memory
);
LUT LUT1(.Addr (TargSel ) ,
.Target (PCTarg )
);
// instruction ROM -- holds the machine code pointed to by program counter
InstROM #(.W(9),.A(10)) IR1(
.InstAddress (PgmCtr ) ,
.InstOut (Instruction)
);
// in place c = a+c ADD R0 R1 R0
// reg file
RegFile #(.W(8),.D(3)) RF1 ( // D(3) makes this 8 elements deep
.Clk ,
.WriteEn (RegWrEn) ,
.RaddrA (Instruction[5:3]), //3'b0 //concatenate with 0 to give us 4 bits
.RaddrB (Instruction[2:0]), // (Instruction[2:0]+1);
.Waddr (Instruction[5:3]), //3'b0 // mux above
.DataIn (RegWriteValue) ,
// outputs
.DataOutA (ReadA ) ,
.DataOutB (ReadB )
);
ALU ALU1 (
.InputA (InA),
.InputB (InB),
.SC_in (PFq),
.OP (Instruction[8:6]),
// outputs
.Out (ALU_out),//regWriteValue),
.PF (PF),
.Zero // status flag; may have others, if desired
);
always @(posedge Clk)
PFq <= PF;
DataMem DM1(
// inputs
.Clk (Clk) ,
.Reset (Reset) ,
.DataAddress (ReadA) ,
.WriteEn (MemWrite),
.DataIn (MemWriteValue),
// outputs
.DataOut (MemReadValue)
);
endmodule

71
RTL/TopLevel_tb.sv Normal file
View File

@ -0,0 +1,71 @@
// Create Date: 2017.01.25
// Design Name: TopLevel Test Bench
// Module Name: TopLevel_tb.v
// CSE141L
// This is NOT synthesizable; use for logic simulation only
// Verilog Test Fixture created for module: TopLevel
module TopLevel_tb; // Lab 17
// To DUT Inputs
bit Init = 'b1,
Req, // start
Clk; // logic Clk; // inits to 1'bx
// logic Clk = 1'b0;
// From DUT Outputs
wire Ack; // done flag
// Instantiate the Device Under Test (DUT)
TopLevel DUT (
.Reset (Init) ,
.Start (Req ) ,
.Clk (Clk ) ,
.Ack (Ack )
);
initial begin
#10ns Init = 'b0;
#10ns Req = 'b1;
// Initialize DUT's data memory
#10ns for(int i=0; i<256; i++) begin
DUT.DM1.Core[i] = 8'h0; // clear data_mem
end
DUT.DM1.Core[1] = 8'h03; // MSW of operand A
DUT.DM1.Core[2] = 8'hff;
DUT.DM1.Core[3] = 8'hff; // MSW of operand B
DUT.DM1.Core[4] = 8'hfb;
DUT.DM1.Core[128] = 8'h00; // preload constants
// ...
DUT.DM1.Core[255] = 8'h00;
// students may also pre_load desired constants into DM
// Initialize DUT's register file
for(int j=0; j<16; j++)
DUT.RF1.Registers[j] = 8'b0; // default -- clear it
// students may pre-load desired constants into the reg_file
// $readmemb("machine_code.txt",inst);
// for(int k=0; k<1024; k++)
// DUT.IR1.inst_rom[k] = inst[k];
// launch prodvgram in DUT
#10ns Req = 0;
// Wait for done flag, then display results
wait (Ack);
#10ns $displayh(DUT.DM1.Core[5],
DUT.DM1.Core[6],"_",
DUT.DM1.Core[7],
DUT.DM1.Core[8]);
// $display("instruction = %d %t",DUT.PC,$time);
#10ns $stop; // $finish;
end
always begin // clock period = 10 Verilog time units
#5ns Clk = 'b1;
#5ns Clk = 'b0;
end
endmodule
// always #5ns Clk = ~Clk;

34
RTL/prog_ctr2.sv Normal file
View File

@ -0,0 +1,34 @@
// Design Name: basic_proc
// Module Name: InstFetch
// Project Name: CSE141L
// Description: instruction fetch (pgm ctr) for processor
//
// Revision: 2019.01.27
//
module InstFetch(
input Reset, // reset, init, etc. -- force PC to 0
Start, // begin next program in series (request issued by test bench)
Clk, // PC can change on pos. edges only
BranchAbs, // jump unconditionally to Target value
ALU_flag, // flag from ALU, e.g. Zero, Carry, Overflow, Negative (from ARM)
input [9:0] Target, // jump ... "how high?"
output logic [9:0] ProgCtr // the program counter register itself
);
// program counter can clear to 0, increment, or jump
always_ff @(posedge Clk) // or just always; always_ff is a linting construct
if(Reset)
ProgCtr <= 0; // for first program; want different value for 2nd or 3rd
else if(Start) // hold while start asserted; commence when released
ProgCtr <= 0;//ProgCtr;
else if(BranchAbs) // unconditional absolute jump
ProgCtr <= Target; // how would you make it conditional and/or relative?
else
ProgCtr <= ProgCtr+'b1; // default increment (no need for ARM/MIPS +4 -- why?)
endmodule
/* Note about Start: if your programs are spread out, with a gap in your machine code listing, you will want
to make Start cause an appropriate jump. If your programs are packed sequentially, such that program 2 begins
right after Program 1 ends, then you won't need to do anything special here.
*/