populate better starting rtl code
This commit is contained in:
parent
31b077c5f2
commit
be156765f3
95
RTL/ALU.sv
95
RTL/ALU.sv
@ -1,55 +1,46 @@
|
||||
// Create Date: 2018.10.15
|
||||
// Module Name: ALU
|
||||
// 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
|
||||
// Additional Comments:
|
||||
// combinational (unclocked) ALU
|
||||
|
||||
always_comb
|
||||
op_mnemonic = op_mne'(OP); // displays operation name in waveform viewer
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
/* InputA=10101010 SC_in = 1
|
||||
kLSH Out = 01010101
|
||||
*/
|
||||
// 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
|
||||
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
|
||||
);
|
||||
|
||||
always_comb begin
|
||||
// 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)
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
|
60
RTL/Ctrl.sv
60
RTL/Ctrl.sv
@ -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 ,
|
||||
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"
|
||||
input[ 8:0] Instruction, // machine code
|
||||
input[ 7:0] DatMemAddr,
|
||||
output logic Branch ,
|
||||
BranchEn ,
|
||||
RegWrEn , // write to reg_file (common)
|
||||
MemWrEn , // write to mem (store only)
|
||||
LoadInst , // mem or ALU to reg_file ?
|
||||
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
|
||||
|
||||
|
@ -1,39 +1,50 @@
|
||||
// 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
|
||||
// Create Date: 2017.01.25
|
||||
// Design Name: CSE141L
|
||||
// Module Name: DataMem
|
||||
// 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, // 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
|
||||
);
|
||||
|
||||
// 8x256 two-dimensional array -- the memory itself
|
||||
logic [W-1:0] core[2**A];
|
||||
|
||||
// reads are combinational
|
||||
always_comb
|
||||
DataOut = core[DataAddress];
|
||||
|
||||
// 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
|
||||
// Preload desired constants into data_mem[128:255]
|
||||
core[128] <= 'b1;
|
||||
core[129] <= 'hff;
|
||||
core[130] <= 'd64;
|
||||
end
|
||||
else if(WriteEn) // store
|
||||
// Do the actual writes
|
||||
core[DataAddress] <= DataIn;
|
||||
endmodule
|
||||
|
@ -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
|
||||
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
|
||||
// ADD = 3'b000; LSH = 3'b001; etc. 3'b111 is undefined here
|
||||
typedef enum logic[2:0] {
|
||||
ADD, LSH, RSH, XOR,
|
||||
AND, SUB, CLR } op_mne;
|
||||
|
||||
endpackage // definitions
|
||||
|
26
RTL/Immediate_LUT.sv
Normal file
26
RTL/Immediate_LUT.sv
Normal 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
|
@ -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
|
||||
|
@ -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
|
@ -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];
|
||||
|
151
RTL/TopLevel.sv
151
RTL/TopLevel.sv
@ -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
|
@ -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
|
@ -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
35
RTL/dummy_DUT.sv
Normal 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
|
||||
|
@ -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
156
RTL/program1_tb1.sv
Normal 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
|
Reference in New Issue
Block a user