populate better starting rtl code

This commit is contained in:
Arthur Lu 2022-08-13 15:34:01 -07:00
parent 31b077c5f2
commit be156765f3
14 changed files with 374 additions and 496 deletions

View File

@ -1,55 +1,46 @@
// 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"
// includes package "Definitions"
// be sure to adjust "Definitions" to match your final set of ALU opcodes
import Definitions::*;
module ALU #(parameter W=8)(
input [W-1:0] InputA, // data inputs
InputB,
input op_mne OP, // ALU opcode, part of microcode
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
output logic [W-1:0] Out, // data output
output logic Zero, // output = zero flag !(Out)
Parity, // outparity flag ^(Out)
Odd, // output odd flag (Out[0])
SC_out // shift or carry out
// you may provide additional status flags, if desired
// comment out or delete any you don't need
);
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
// No Op = default
// add desired ALU ops, delete or comment out any you don't need
Out = 8'b0; // don't need NOOP? Out = 8'bx
SC_out = 1'b0; // will flag any illegal opcodes
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
ADD : {SC_out,Out} = InputA + InputB + SC_in; // unsigned add with carry-in and carry-out
LSH : {SC_out,Out} = {InputA[7:0],SC_in}; // shift left, fill in with SC_in, fill SC_out with InputA[7]
// 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
RSH : {Out,SC_out} = {SC_in, InputA[7:0]}; // shift right
XOR : Out = InputA ^ InputB; // bitwise exclusive OR
AND : Out = InputA & InputB; // bitwise AND
SUB : {SC_out,Out} = InputA + (~InputB) + 1; // InputA - InputB;
CLR : {SC_out,Out} = 'b0;
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
assign Zero = ~|Out; // reduction NOR Zero = !Out;
assign Parity = ^Out; // reduction XOR
assign Odd = Out[0]; // odd/even -- just the value of the LSB
endmodule
/* InputA=10101010 SC_in = 1
kLSH Out = 01010101
*/

View File

@ -1,25 +1,53 @@
// CSE141L
import definitions::*;
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 ,
output logic Branch ,
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"
TapSel ,
Ack , // "done w/ program"
output logic[1:0] PCTarg,
// output logic[2:0] ALU_inst
);
/* ***** All numerical values are completely arbitrary and for illustration only *****
*/
// alternative -- case format
always_comb begin
// list the defaults here
Branch = 'b0;
BranchEn = 'b0;
RegWrEn = 'b1;
MemWrEn = 'b0;
LoadInst = 'b0;
TapSel ' 'b0; //
PCTarg = 'b0; // branch "where to?"
case(Instruction[8:6]) // list just the exceptions
3'b000: begin
MemWrEn = 'b1; // store, maybe
RegWrEn = 'b0;
end
3'b001: LoadInst = 'b1; // load
3'b010: begin end
3'b011: begin end
3'b100: begin end
3'b101: begin end
3'b110: begin end
// no default case needed -- covered before "case"
endcase
end
assign Ack = ProgCtr == 971;
// alternative Ack = Instruction == 'b111_000_111
// ALU commands
//assign ALU_inst = Instruction[2:0];
@ -35,12 +63,12 @@ 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;
// equiv to simply: assign Jump = Instruction[2:0] == RSH;
always_comb
if(Instruction[2:0] == kRSH)
Jump = 1;
if(Instruction[2:0] == RSH)
Branch = 1;
else
Jump = 0;
Branch = 0;
// branch every time instruction = 9'b?????1111;
assign BranchEn = &Instruction[3:0];
@ -50,7 +78,7 @@ assign BranchEn = &Instruction[3:0];
assign PCTarg = Instruction[3:2];
// reserve instruction = 9'b111111111; for Ack
assign Ack = &Instruction;
assign Ack = &Instruction; // = ProgCtr == 385;
endmodule

View File

@ -1,39 +1,50 @@
// Create Date: 2017.01.25
// Revision: 2022.05.04 made data width parametric
// Design Name:
// Design Name: CSE141L
// Module Name: DataMem
// single address pointer for both read and write
// CSE141L
module DataMem #(parameter W=8, D=8)(
// Last Update: 2022.01.13
// Memory can only read (LDR) or write (STR) on each Clk cycle, so there is a single
// address pointer for both read and write operations.
//
// Parameters:
// - A: Address Width. This controls the number of entries in memory
// - W: Data Width. This controls the size of each entry in memory
// This memory can hold `(2**A) * W` bits of data.
//
// WI22 is a 256-entry single-byte (8 bit) data memory.
module DataMem #(parameter W=8, A=8) ( // do not change W=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);
Reset, // initialization
WriteEn, // write enable
input [A-1:0] DataAddress, // A-bit-wide pointer to 256-deep memory
input [W-1:0] DataIn, // W-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
// 8x256 two-dimensional array -- the memory itself
logic [W-1:0] core[2**A];
always_comb // reads are combinational
DataOut = Core[DataAddress];
// reads are combinational
always_comb
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) ...
// writes are sequential
always_ff @ (posedge Clk)
/*
// 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) ... 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
// Preload desired constants into data_mem[128:255]
core[128] <= 'b1;
core[129] <= 'hff;
core[130] <= 'd64;
end
else if(WriteEn)
Core[DataAddress] <= DataIn;
else if(WriteEn) // store
// Do the actual writes
core[DataAddress] <= DataIn;
endmodule

View File

@ -1,24 +1,16 @@
//This file defines the parameters used in the alu
// CSE141L
// Rev. 2020.5.27
// Rev. 2022.5.27
// import package into each module that needs it
// packages very useful for declaring global variables
package definitions;
// need > 8 instructions?
// typedef enum logic[3:0] and expand the list of enums
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
// ADD = 3'b000; LSH = 3'b001; etc. 3'b111 is undefined here
typedef enum logic[2:0] {
ADD, LSH, BSH, XOR,
ADD, LSH, RSH, 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

26
RTL/Immediate_LUT.sv Normal file
View File

@ -0,0 +1,26 @@
/* CSE141L
possible lookup table for PC target
leverage a few-bit pointer to a wider number
Lookup table acts like a function: here Target = f(Addr);
in general, Output = f(Input); lots of potential applications
*/
module Immediate_LUT #(PC_width = 10)(
input [ 2:0] addr,
output logic[PC_width-1:0] datOut
);
always_comb begin
datOut = 'h001; // default to 1 (or PC+1 for relative)
case(addr)
2'b00: datOut = 'hfc; // -4, i.e., move back 16 lines of machine code
2'b01: datOut = 'h03;
2'b10: datOut = 'h07;
endcase
end
endmodule
// 3fc = 1111111100 -4
// PC 0000001000 8
// 0000000100 4

View File

@ -3,13 +3,13 @@
// Project Name: CSE141L
// Description: instruction fetch (pgm ctr) for processor
//
// Revision: 2019.01.27
// Revision: 2021.11.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
BranchAbs, // jump conditionally 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?"
@ -19,13 +19,14 @@ module InstFetch #(parameter T=10)( // PC width -- up to 32, if you like
// 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
// program counter can clear to 0, increment, or branch
// for unconditional branching, "ALU_flag" input should be driven by 1
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
else if(BranchAbs && ALU_flag // 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

View File

@ -1,32 +0,0 @@
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

View File

@ -6,14 +6,18 @@
// Description: Verilog module -- instruction ROM template
// preprogrammed with instruction values (see case statement)
//
// Revision: 2020.08.08
// Revision: 2021.08.08
//
// A = program counter width
// W = machine code width -- do not change for CSE141L
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
// This version will work best with assemblers, but you can try the alternative starting line 33
// This version is also by far the easiest if you have a long program scrip.
// 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];

View File

@ -1,151 +0,0 @@
// 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

View File

@ -1,78 +0,0 @@
// 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

View File

@ -1,71 +0,0 @@
// 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;

35
RTL/dummy_DUT.sv Normal file
View File

@ -0,0 +1,35 @@
// skeletal starter code top level of your DUT
module top_level(
input clk, init, req,
output logic ack);
logic mem_wen;
logic[7:0] mem_addr,
mem_in,
mem_out;
logic[11:0] pctr; // temporary program counter
// populate with program counter, instruction ROM, reg_file (if used),
// accumulator (if used),
DataMem DM(.Clk (clk),
.Reset (init),
.WriteEn (mem_wen),
.DataAddress (mem_addr),
.DataIn (mem_in),
.DataOut (mem_out));
// temporary circuit to provide ack (done) flag to test bench
// remove or greatly increase the match value once you get a
// proper ack
always @(posedge clk)
if(init || req)
pctr <= 'h0;
else
pctr <= pctr+'h1;
assign ack = pctr=='h256; // pctr needed to trigger ack (arbitary time)
endmodule

View File

@ -1,34 +0,0 @@
// 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.
*/

156
RTL/program1_tb1.sv Normal file
View File

@ -0,0 +1,156 @@
// program1_tb
// testbench for programmable message encryption (Program #1)
// CSE141L
// runs program 1 (encrypt a message)
module encrypt_tb () ;
// DUT interface -- four one-bit wires, three to DUT, one from
bit clk , // advances simulation step-by-step
init = 1'b1 , // init (reset) command to DUT
start = 1'b1 ; // request (start program) command to DUT
wire done ; // acknowledge (program done) flag returned by DUT
// test bench parameters
logic[7:0] pre_length ; // number of space char. before message itself, sent to data_mem[61]
logic[7:0] message1[54] , // original raw message, in binary, up to 54 characters in length
msg_padded1[64], // original message, plus pre- and post-padding w/ ASCII spaces
msg_crypto1[64]; // encrypted message returned by DUT
logic[7:0] lfsr_ptrn , // choses one of 9 maximal length 7-tap shift reg. patterns
LFSR_ptrn[9] , // the 9 candidate maximal-length 7-bit LFSR tap ptrns themselves
lfsr1[64] , // states of program 1 encrypting LFSR
LFSR_init ; // one of 127 possible NONZERO starting states for encrypting LFSR
int score ; // count of correctly encyrpted characters
// our original American Standard Code for Information Interchange message follows
// note in practice your design should be able to handle ANY ASCII string that is
// restricted to characters between space (0x20) and script f (0x9f) and shorter than
// 55 characters in length
string str1 = "Mr. Watson, come here. I want to see you."; // sample program 1 input
// string str1 = " Knowledge comes, but wisdom lingers. "; // alternative inputs
// string str1 = " 01234546789abcdefghijklmnopqrstuvwxyz. "; // (make up your own,
// string str1 = " f A joke is a very serious thing."; // as well)
// string str1 = " Ajoke "; //
// string str1 = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
// string str1 = " Knowledge comes, but wisdom lingers. "; //
// displayed encrypted string will go here:
string str_enc1[64]; // program 1 desired output will go here
int strlen; // length of incoming message string itself, before padding
logic[3:0] pt_no; // select LFSR pattern, index value 0 through 8
int file_no; // output file tag (set to 1 for write to console/transcript)
// the 9 possible 7-tap maximal-length feedback tap patterns from which to choose
assign LFSR_ptrn[0] = 8'h60; // (0)110_0000
assign LFSR_ptrn[1] = 8'h48;
assign LFSR_ptrn[2] = 8'h78;
assign LFSR_ptrn[3] = 8'h72;
assign LFSR_ptrn[4] = 8'h6A;
assign LFSR_ptrn[5] = 8'h69;
assign LFSR_ptrn[6] = 8'h5C;
assign LFSR_ptrn[7] = 8'h7E; // (0)111_1110
assign LFSR_ptrn[8] = 8'h7B;
always_comb begin
pt_no = 0;//$random; // or select a specific pattern ([0] and [1] are simplest to debug
if(pt_no>8) pt_no[3] = 0; // restrict pt_no to 0 through 8
lfsr_ptrn = LFSR_ptrn[pt_no]; // look up and engage the selected pattern; to data_mem[62]
end
// now select a starting LFSR state -- any nonzero value will do
always_comb begin
LFSR_init = 'b1;//$random>>2; // or set a specific value, such as 7'b1, for easier debug
if(!LFSR_init) LFSR_init = 7'b1; // prevents illegal starting state = 7'b0;
end
// set preamble length for the program run (always > 9 but < 26)
always_comb begin
pre_length = 10;//$random>>10 ; // number of space characters ahead of message; the >>10 changes the random value
if(pre_length < 10) pre_length = 10; // prevents pre_length < 10
else if(pre_length > 26) pre_length = 26;
end
// ***** instantiate your own top level design here *****
top_level dut(
.clk (clk ), // input: use your own port names, if different
.init (init ), // input: some prefer to call this ".reset"
.req (start), // input: launch program
.ack (done ) // output: "program run complete"
);
initial begin
//***** pre-load your instruction ROM here or inside itself *****
// $readmemb("encoder.bin", dut.instr_rom.rom);
// you may also pre-load desired constants, etc. into
// your data_mem here -- the upper addresses are reserved for your use
// dut.data_mem.DM[128]=8'hfe; //whatever constants you want
// to display to console, change this line to file_no = 'b1;
file_no = 'b1; // display to console instead of file
// file_no = $fopen("msg_enocder_out.txt","w"); // create your output file
#0ns strlen = str1.len; // length of string 1 (# characters between " ")
if(strlen>54) strlen = 54; // clip message at 54 characters
// program 1 -- precompute encrypted message
lfsr1[0] = LFSR_init; // any nonzero value (zero may be helpful for debug)
$fdisplay(file_no,"run encryption program; original message = ");
$fdisplay(file_no,"%s",str1); // print original message in transcript window
$fdisplay(file_no,"pt_no = %d",pt_no);
$fdisplay(file_no,"LFSR_ptrn = 0x%h, LFSR_init = 0x%h",lfsr_ptrn,LFSR_init);
// will subtract 0x20 from each preamble and each message character
for(int j=0; j<64; j++) // pre-fill message_padded with ASCII space characters
msg_padded1[j] = 8'h20; //
for(int l=0; l<strlen; l++) // overwrite up to 54 of these spaces w/ message itself
msg_padded1[pre_length+l] = str1[l]; // test bench does the -0x20 offset now
// compute and store the LFSR sequence
for (int ii=0;ii<63;ii++)
lfsr1[ii+1] = {(lfsr1[ii][5:0]),(^(lfsr1[ii]&lfsr_ptrn))};
// encrypt the message character-by-character, then prepend the parity
// testbench will change on falling clocks to avoid race conditions at rising clocks
for (int i=0; i<64; i++) begin
msg_crypto1[i] = ((msg_padded1[i]-32) ^ lfsr1[i]);
msg_crypto1[i][7] = 'b0;//^msg_crypto1[i][6:0]; // prepend parity bit into MSB
$fdisplay(file_no,"i=%d, msg_pad=0x%h, lfsr=%b msg_crypt w/ parity = 0x%h",
i,msg_padded1[i],lfsr1[i],msg_crypto1[i]);
// for display purposes only, add 8'h20 to avoid nonprintable characters (<8'h20)
str_enc1[i] = string'(msg_crypto1[i][6:0]+'h20);
end
$fdisplay(file_no,"encrypted string = ");
for(int jj=0; jj<64; jj++)
$fwrite(file_no,"%s",str_enc1[jj]);
$fdisplay(file_no,"\n");
// run encryption program
// ***** load operands into your data memory *****
// ***** use your instance name for data memory and its internal core *****
for(int m=0; m<61; m++)
dut.DM.core[m] = 8'h0; // pad memory w/ ASCII space characters
for(int m=0; m<strlen; m++)
dut.DM.core[m] = (str1[m]-8'h20); // overwrite/copy original string into device's data memory[0:strlen-1]
dut.DM.core[61] = pre_length; // number of bytes preceding message
dut.DM.core[62] = lfsr_ptrn; // LFSR feedback tap positions (9 permissible patterns)
dut.DM.core[63] = LFSR_init; // LFSR starting state (nonzero)
#20ns init = 1'b0; // suggestion: reset = 1 forces your program counter to 0
#10ns start = 1'b0; // request/start = 1 holds your program counter
#60ns; // wait for 6 clock cycles of nominal 10ns each
wait(done); // wait for DUT's ack/done flag to go high
// #2000ns;
#10ns $fdisplay(file_no,"");
$fdisplay(file_no,"program 1:");
// ***** reads your results and compares to test bench
// ***** use your instance name for data memory and its internal core *****
// the +'h20 restores the -32 bias, for better display visuals
for(int n=0; n<64; n++) begin
if(msg_crypto1[n]==dut.DM.core[n+64]) begin
$fdisplay(file_no,"%d bench msg: %s %h dut msg: %h",
n, msg_crypto1[n][6:0]+8'h20, msg_crypto1[n], dut.DM.core[n+64]);
score++;
end
else
$fdisplay(file_no,"%d bench msg: %s %h dut msg: %h OOPS!",
n, msg_crypto1[n][6:0]+8'h20, msg_crypto1[n], dut.DM.core[n+64]);
end
$fdisplay(file_no,"score = %d/64",score);
#20ns $fclose(file_no);
#20ns $stop;
end
always begin // continuous loop
#5ns clk = 1; // clock tick
#5ns clk = 0; // clock tock
end
endmodule