Merge pull request #2 from supleed2/bus_wrapper

Merge Bus Version and updated testcases to Main
This commit is contained in:
Aadi Desai 2020-12-17 18:25:17 +00:00 committed by GitHub
commit 099540f6ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1385 additions and 958 deletions

1
.gitignore vendored
View file

@ -3,5 +3,6 @@ exec/*
*.log.txt
*.out.txt
mips_cpu_harvard.vcd
mips_cpu_bus.vcd
.DS_Store
inputs/.DS_Store

View file

@ -1,7 +1,6 @@
3C041234
34045678
3C05BFC0
3405001C
3405101C
A0A40000
80A20000
00000008
00000008

View file

@ -1,7 +1,6 @@
3C041234
34045678
3C05BFC0
3405001C
3405101C
A4A40000
84A40000
00000008
84A20000
00000008

1
inputs/sh/sh-2.ref.txt Normal file
View file

@ -0,0 +1 @@
22136

6
inputs/sh/sh-2.txt Normal file
View file

@ -0,0 +1,6 @@
3C041234
34045678
3405101C
A4A40000
8CA20000
00000008

File diff suppressed because it is too large Load diff

231
rtl/mips_cpu_bus.v Normal file
View file

@ -0,0 +1,231 @@
module mips_cpu_bus(
/* Standard signals */
input logic clk,
input logic reset,
output logic active,
output logic[31:0] register_v0,
/* Avalon memory mapped bus controller (master) */
output logic[31:0] address,
output logic write,
output logic read,
input logic waitrequest,
output logic[31:0] writedata,
output logic[3:0] byteenable,
input logic[31:0] readdata
);
logic[1:0] state; // current state of cpu within cycle
logic[1:0] n_state; // state to be set at next clk edge
logic[31:0] instr_reg; // instruction register / single-word cache for current instruction
logic clk_internal; // modulated clock to be passed to harvard cpu
logic[31:0] harvard_instr_address; // instr addr from pc
logic harvard_read; // harvard cpu read flag
logic harvard_write; // harvard cpu write flag
logic[31:0] harvard_data_address; // data addr from ALU
logic[31:0] harvard_readdata; // <= data read from Avalon MM Device
logic[31:0] harvard_writedata; // data to be written to Avalon MM Device
logic[3:0] write_byteenable; // byteenable calculator for partial write
logic clk_state; // make sure posedge and negedge of clk do not occur repeatedly
logic partial_write; // flag to control datapath when doing a partial write
logic[31:0] partial_writedata; // modified data for partial writes (StoreHalfword or StoreByte)
logic[31:0] write_data_address; // modified data address for partial writes
logic clk_enable; // unused floating wire
initial begin
clk_internal = 1'b0;
n_state = 2'b00;
state = 2'b00;
instr_reg = 32'h00000000;
address = 32'h00000000;
write = 1'b0;
read = 1'b0;
writedata = 32'h00000000;
byteenable = 4'b0000;
clk_state = 0;
end
always_ff @(negedge reset) begin // kickstart clock after reset
clk_internal <= 1'b1;
state <= 2'b00;
end
always_ff @(posedge clk) begin // CLK Rising Edge
if (!waitrequest && !clk_state) begin
case (n_state)
2'b00: begin // fetch
clk_internal <= 1'b1;
state <= 2'b00;
end
2'b01: begin // execute
state <= 2'b01;
instr_reg <= readdata;
end
2'b10: begin // read
state <= 2'b10;
end
2'b11: begin // write
state <= 2'b11;
end
endcase // state
end
clk_state <= 1'b1;
end
always_ff @(negedge clk) begin // CLK Falling Edge
if (!waitrequest && clk_state) begin
case (state)
2'b00: begin // nothing happens on fetch negedge
end
2'b01: begin // execute negedge
if (!harvard_read && !harvard_write) begin // instruction complete, trigger writeback
clk_internal <= 1'b0;
end // otherwise do nothing
end
2'b10: begin
clk_internal <= 1'b0;
end
2'b11: begin
clk_internal <= 1'b0;
end
endcase
end
clk_state <= 1'b0;
end
always @(*) begin
case (instr_reg[31:26])
6'b101000: begin // Store Byte
partial_write = 1'b1;
case (harvard_data_address[1:0])
2'b00: begin
partial_writedata = {{24{1'b0}}, harvard_writedata[7:0]};
write_byteenable = 4'b0001;
write_data_address = {harvard_data_address[31:2], 2'b00};
end
2'b01: begin
partial_writedata = {{16{1'b0}}, harvard_writedata[7:0], {8{1'b0}}};
write_byteenable = 4'b0010;
write_data_address = {harvard_data_address[31:2], 2'b00};
end
2'b10: begin
partial_writedata = {{8{1'b0}}, harvard_writedata[7:0], {16{1'b0}}};
write_byteenable = 4'b0100;
write_data_address = {harvard_data_address[31:2], 2'b00};
end
2'b11: begin
partial_writedata = {harvard_writedata[7:0], {24{1'b0}}};
write_byteenable = 4'b1000;
write_data_address = {harvard_data_address[31:2], 2'b00};
end
endcase
end
6'b101001: begin // Store Halfword
partial_write = 1'b1;
case (harvard_data_address[1:0])
2'b00: begin
partial_writedata = {{16{1'b0}}, harvard_writedata[15:0]};
write_byteenable = 4'b0011;
write_data_address = {harvard_data_address[31:2], 2'b00};
end
2'b01: begin // halfword address must be matrually aligned, last bit must be 0
partial_writedata = 32'hxxxxxxxx;
write_byteenable = 4'bxxxx;
write_data_address = 32'hxxxxxxxx;
end
2'b10: begin
partial_writedata = {harvard_writedata[15:0], {16{1'b0}}};
write_byteenable = 4'b1100;
write_data_address = {harvard_data_address[31:2], 2'b00};
end
2'b11: begin // halfword address must be matrually aligned, last bit must be 0
partial_writedata = 32'hxxxxxxxx;
write_byteenable = 4'bxxxx;
write_data_address = 32'hxxxxxxxx;
end
endcase
end
default: begin // Store Word OR All other instructions (These flags are ignored outside the write state)
partial_write = 1'b0;
partial_writedata = 32'h00000000;
write_byteenable = 4'b1111;
write_data_address = harvard_data_address;
end
endcase
end
always_comb begin
if (reset) begin
clk_internal = 1'b0;
n_state = 2'b00;
state = 2'b00;
instr_reg = 32'h00000000;
address = 32'h00000000;
write = 1'b0;
read = 1'b0;
writedata = 32'h00000000;
byteenable = 4'b0000;
end else begin
case (state)
2'b00: begin // connecting wires when in fetch state
address = harvard_instr_address;
read = 1'b1;
write = 1'b0;
byteenable = 4'b1111;
harvard_readdata = 32'h00000000;
writedata = 32'h00000000;
n_state = 2'b01;
end
2'b01: begin // connecting wires when in execute state
address = 32'h00000000;
read = 1'b0;
write = 1'b0;
byteenable = 4'b0000;
harvard_readdata = 32'h00000000;
writedata = 32'h00000000;
if (harvard_read) begin
n_state = 2'b10; // next state is read
end else if (harvard_write) begin
n_state = 2'b11; // next state is write
end else begin
n_state = 2'b00; // next state is fetch
end
end
2'b10: begin // connecting wires when in read state
address = harvard_data_address;
read = clk_internal ? 1'b1 : 1'b0;
write = 1'b0;
byteenable = 4'b1111;
harvard_readdata = readdata;
writedata = 32'h00000000;
n_state = 2'b00;
end
2'b11: begin // connecting wires when in write state
address = write_data_address;
read = 1'b0;
write = 1'b1;
byteenable = write_byteenable;
harvard_readdata = 32'h00000000;
writedata = partial_write ? partial_writedata : harvard_writedata;
n_state = 2'b00;
end
endcase // state
end
end
mips_cpu_harvard mips_cpu_harvard( // Harvard CPU within wrapper
.clk(clk_internal), // modulated clock input to allow waiting for valid data from memory, input
.reset(reset), // CPU reset, input
.active(active), // Is CPU active, output
.register_v0(register_v0), // $2 / $v0 debug bus, output
.clk_enable(clk_enable), // unused clock enable, input
.instr_address(harvard_instr_address), // instr addr from pc, output
.instr_readdata(instr_reg), // cached instruction passed into harvard cpu, input
.data_address(harvard_data_address), // harvard data memory address, output
.data_write(harvard_write), // harvard write flag, output
.data_read(harvard_read), // harvard read flag, output
.data_writedata(harvard_writedata), // data output from regfile readport2, output
.data_readdata(harvard_readdata) // data in from read instruction, input
);
endmodule

View file

@ -117,6 +117,8 @@ always @(*) begin
CtrlMemtoReg = 3'd3;//write data port of regfile is fed from ALUHi
end else if ((op==SPECIAL)&&(funct == MFLO))begin
CtrlMemtoReg = 3'd4;//write data port of regfile is fed from ALULo
end else if (((op==SPECIAL)&&(funct == JR)) || (op == BEQ) || (op==SW) ||((op==REGIMM)&&(rt==BGEZ)) || (op==BGTZ) || ((op==REGIMM)&&(rt==BLTZ)) || (op==BLEZ) || (op==BNE) || (op==J) || ((op==SPECIAL)&&(funct==MTHI)) || ((op==SPECIAL)&&(funct==MTLO)) || ((op==SPECIAL)&&(funct==MULT)) || ((op==SPECIAL)&&(funct==MULTU)) || ((op==SPECIAL)&&(funct==DIV)) || ((op==SPECIAL)&&(funct==DIVU)) || (op==SB) || (op==SH))begin
CtrlMemRead = 0;//Read disabled during jump
end else begin CtrlMemRead = 1'bx;end//Not all instructions are encompassed so, added incase for debug purposes
//CtrlALUOp Logic

View file

@ -9,6 +9,7 @@ reg[31:0] npc_curr;
initial begin
npc_curr = (32'hBFC00000 + 32'd4);
npc_out = 32'hBFC00000;
end // initial
always_comb begin

View file

@ -1,4 +1,60 @@
#!/bin/bash
# should not create any files in the rtl dir
# but auxiliary files / dirs can be utilised
SRC_DIR=${1?Error: no source directory given in argument};
SRC=$(find ./${SRC_DIR}/*);
SRC_TEMP="";
for src in ${SRC}
do
SRC_TEMP+=${src}" ";
done
SRC=${SRC_TEMP};
INSTR=${2:-"No instruction specified: running all testcases"};
if [[ ${INSTR} == "No instruction specified: running all testcases" ]];
then
for DIR in inputs/*/
do
DIR=$(basename ${DIR});
LOOP=$(find inputs/${DIR}/* ! -name '*ref*' ! -name '*log*' ! -name '*data*' ! -name '*out*');
for TESTCASE in ${LOOP}
do
TESTCASE=$([[ ${TESTCASE} =~ /([^./]+)\. ]] && echo "${BASH_REMATCH[1]}");
iverilog -Wall -g2012 \
-s mips_cpu_bus_tb \
-P mips_cpu_bus_tb.INSTR_INIT_FILE=\"inputs/${DIR}/${TESTCASE}.txt\" \
-P mips_cpu_bus_tb.DATA_INIT_FILE=\"inputs/${DIR}/${TESTCASE}.data.txt\" \
-o exec/mips_cpu_bus_tb_${TESTCASE} testbench/mips_cpu_bus_tb.v testbench/mips_cpu_bus_memory.v \
${SRC} 2> /dev/null
./exec/mips_cpu_bus_tb_${TESTCASE} &> ./inputs/${DIR}/${TESTCASE}.log.txt; # log file for debugging (contains $display)
echo "$(tail -1 ./inputs/${DIR}/${TESTCASE}.log.txt)" > ./inputs/${DIR}/${TESTCASE}.out.txt; # register v0 output to compare with reference
if diff -w ./inputs/${DIR}/${TESTCASE}.out.txt ./inputs/${DIR}/${TESTCASE}.ref.txt &> /dev/null # compare
then
echo ${TESTCASE} ${DIR} "Pass";
else
printf '%s %s %s%d %s%d%s\n' "${TESTCASE}" "${DIR}" "Fail Output=" "$(tail -1 ./inputs/${DIR}/${TESTCASE}.out.txt)" "Ref=" "$(tail -1 ./inputs/${DIR}/${TESTCASE}.ref.txt)" 2> /dev/null;
fi
done
done
else
LOOP=$(find inputs/${INSTR}/* ! -name '*ref*' ! -name '*log*' ! -name '*data*' ! -name '*out*');
for TESTCASE in ${LOOP}
do
TESTCASE=$([[ ${TESTCASE} =~ /([^./]+)\. ]] && echo "${BASH_REMATCH[1]}");
iverilog -Wall -g2012 \
-s mips_cpu_bus_tb \
-P mips_cpu_bus_tb.INSTR_INIT_FILE=\"inputs/${INSTR}/${TESTCASE}.txt\" \
-P mips_cpu_bus_tb.DATA_INIT_FILE=\"inputs/${INSTR}/${TESTCASE}.data.txt\" \
-o exec/mips_cpu_bus_tb_${TESTCASE} testbench/mips_cpu_bus_tb.v testbench/mips_cpu_bus_memory.v \
${SRC} 2> /dev/null
./exec/mips_cpu_bus_tb_${TESTCASE} &> ./inputs/${INSTR}/${TESTCASE}.log.txt; # log file for debugging (contains $display)
echo "$(tail -1 ./inputs/${INSTR}/${TESTCASE}.log.txt)" > ./inputs/${INSTR}/${TESTCASE}.out.txt; # register v0 output to compare with reference
if diff -w ./inputs/${INSTR}/${TESTCASE}.out.txt ./inputs/${INSTR}/${TESTCASE}.ref.txt &> /dev/null # compare
then
echo ${TESTCASE} ${INSTR} "Pass";
else
printf '%s %s %s%d %s%d%s\n' "${TESTCASE}" "${INSTR}" "Fail Output=" "$(tail -1 ./inputs/${INSTR}/${TESTCASE}.out.txt)" "Ref=" "$(tail -1 ./inputs/${INSTR}/${TESTCASE}.ref.txt)" 2> /dev/null;
fi
done
fi

View file

@ -47,7 +47,7 @@ else
-P mips_cpu_harvard_tb.INSTR_INIT_FILE=\"inputs/${INSTR}/${TESTCASE}.txt\" \
-P mips_cpu_harvard_tb.DATA_INIT_FILE=\"inputs/${INSTR}/${TESTCASE}.data.txt\" \
-o exec/mips_cpu_harvard_tb_${TESTCASE} testbench/mips_cpu_harvard_tb.v testbench/mips_cpu_memory.v\
${SRC} #2> /dev/null
${SRC} 2> /dev/null
./exec/mips_cpu_harvard_tb_${TESTCASE} &> ./inputs/${INSTR}/${TESTCASE}.log.txt; # log file for debugging (contains $display)
echo "$(tail -1 ./inputs/${INSTR}/${TESTCASE}.log.txt)" > ./inputs/${INSTR}/${TESTCASE}.out.txt; # register v0 output to compare with reference
if diff -w ./inputs/${INSTR}/${TESTCASE}.out.txt ./inputs/${INSTR}/${TESTCASE}.ref.txt &> /dev/null # compare

View file

@ -0,0 +1,100 @@
module mips_cpu_bus_memory( //Avalon memory mapped bus controller (slave)
input logic clk,
input logic reset,
input logic[31:0] address,
input logic write,
input logic read,
output logic waitrequest,
input logic[31:0] writedata,
input logic[3:0] byteenable,
output logic[31:0] readdata
);
parameter INSTR_INIT_FILE = "";
parameter DATA_INIT_FILE = "";
logic [31:0] data_memory [0:63]; // location 0x00001000 onwards
logic [31:0] instr_memory [0:63]; // location 0xBFC00000 onwards
initial begin
for (integer i=0; i<$size(data_memory); i++) begin //Initialise data to zero by default
data_memory[i] = 0;
end
for (integer i=0; i<$size(instr_memory); i++) begin //Initialise instr to zero by default
instr_memory[i] = 0;
end
if (INSTR_INIT_FILE != "") begin //Load instr contents from file if specified
$display("RAM: Loading RAM contents from %s", INSTR_INIT_FILE);
$readmemh(INSTR_INIT_FILE, instr_memory);
end
for (integer i = 0; i<$size(instr_memory); i++) begin //Read out instr contents to log
$display("byte +%h: %h", 32'hBFC00000+i*4, instr_memory[i]);
end
if (DATA_INIT_FILE != "") begin //Load data contents from file if specified
$display("MEM: Loading MEM contents from %s", DATA_INIT_FILE);
$readmemh(DATA_INIT_FILE, data_memory);
end else begin
$display("MEM FILE NOT GIVEN");
end
for (integer i = 0; i<$size(data_memory); i++) begin //Read out data contents to log
$display("byte +%h: %h", 32'h00001000+i*4, data_memory[i]);
end
waitrequest = 1'b0; // set waitrequest low to begin
readdata = 32'h00000000; // set readdata low to begin
end
always_comb begin
if (reset) begin
waitrequest = 1'b0;
end
end
always_ff @(posedge read or posedge write) begin
waitrequest <= 1'b1;
end
always_ff @(posedge clk) begin
if (waitrequest) begin
if (read) begin
if (address >= 32'hBFC00000) begin // instruction read
readdata <= instr_memory[{address-32'hBFC00000}>>2];
end else if (address >= 32'h00001000) begin // data read
readdata <= data_memory[{address-32'h00001000}>>2];
end
waitrequest <= 1'b0; // end with setting waitrequest low
end else if (write) begin
if (address >= 32'hBFC00000) begin // writing to instr mem area is invalid
$display("Error, write attempted in instr area at address: %h", address);
end else if (address >= 32'h00001000) begin // write to data mem
if (byteenable[3]) begin // if first byte enabled, write
data_memory[{address-32'h00001000}>>2][31:24] <= writedata[31:24];
end
if (byteenable[2]) begin // if second byte enabled, write
data_memory[{address-32'h00001000}>>2][23:16] <= writedata[23:16];
end
if (byteenable[1]) begin // if third byte enabled, write
data_memory[{address-32'h00001000}>>2][15:8] <= writedata[15:8];
end
if (byteenable[0]) begin // if fourth byte enabled, write
data_memory[{address-32'h00001000}>>2][7:0] <= writedata[7:0];
end
waitrequest <= 1'b0; // end with setting waitrequest low
end
end else begin
waitrequest <= 1'bx;
readdata <= 32'hxxxxxxxx;
end
end else begin
waitrequest <= 1'b0;
readdata <= 32'h00000000;
end
end
endmodule

View file

@ -1,61 +1,80 @@
module mips_cpu_bus_tb;
timeunit 1ns / 10ps;
parameter RAM_INIT_FILE = "test/01-binary/countdown.hex.txt";
parameter TIMEOUT_CYCLES = 10000;
parameter INSTR_INIT_FILE = "";
parameter DATA_INIT_FILE = "";
parameter TIMEOUT_CYCLES = 100; // Timeout cycles are higher to account for memory stall delays
logic clk;
logic rst;
logic clk, reset, active, write, read, waitrequest;
logic[31:0] address, register_v0, writedata, readdata;
logic[3:0] byteenable;
logic running;
mips_cpu_bus_memory #(INSTR_INIT_FILE, DATA_INIT_FILE) memInst( //Avalon memory mapped bus controller (slave)
.clk(clk), // clk input to mem
.reset(reset), // reset input to stall mem during cpu reset
.address(address), // addr input to mem
.write(write), // write flag input
.read(read), // read flag input
.waitrequest(waitrequest), // mem stall output
.writedata(writedata), // data to be written
.byteenable(byteenable), // byteenable bus for writes
.readdata(readdata) // read output port
);
logic[11:0] address;
logic write;
logic read;
logic[15:0] writedata;
logic[15:0] readdata;
mips_cpu_bus cpuInst(
.clk(clk), // clk input to cpu wrapper
.reset(reset), // reset input
.active(active), // active output flag
.register_v0(register_v0), // debug $2 or $v0 output bus
.address(address), // mem addr output
.write(write), // mem write output flag
.read(read), // mem read output flag
.waitrequest(waitrequest), // mem stall input flag
.writedata(writedata), // data to write to mem output
.byteenable(byteenable), // bytes to write output
.readdata(readdata) // data from mem input
);
RAM_16x4096_delay1 #(RAM_INIT_FILE) ramInst(clk, address, write, read, writedata, readdata);
CPU_MU0_delay1 cpuInst(clk, rst, running, address, write, read, writedata, readdata);
// Setup and clock
initial begin
$dumpfile("mips_cpu_bus.vcd");
$dumpvars(0,mips_cpu_bus_tb);
clk=0;
// Generate clock
initial begin
clk=0;
repeat (TIMEOUT_CYCLES) begin
#10;
clk = !clk;
#10;
clk = !clk;
end
$fatal(2, "Simulation did not finish within %d cycles.", TIMEOUT_CYCLES);
repeat (TIMEOUT_CYCLES) begin
#10;
clk = !clk;
#10;
clk = !clk;
end
initial begin
rst <= 0;
$fatal(2, "Simulation did not finish within %d cycles.", TIMEOUT_CYCLES);
end
initial begin
reset <= 1;
@(posedge clk);
reset <= 0;
@(posedge clk);
assert(active==1)
else $display("TB : CPU did not set active=1 after reset.");
while (active) begin
//$display("Clk: %d", clk);
@(posedge clk);
rst <= 1;
@(posedge clk);
rst <= 0;
@(posedge clk);
assert(running==1)
else $display("TB : CPU did not set running=1 after reset.");
while (running) begin
@(posedge clk);
end
$display("TB : finished; running=0");
$finish;
//$display("Register v0: %d", register_v0);
//$display("Reg File Write data: %d", cpuInst.in_writedata);
$display("Reg File Out Read data: %h", cpuInst.mips_cpu_harvard.out_readdata1);
$display("Reg File opcode: %b", cpuInst.mips_cpu_harvard.regfile.opcode);
//$display("ALU output: %h", cpuInst.out_ALURes);
//$display("ALU input B: %h", cpuInst.alu.B);
end
@(posedge clk);
$display("TB: CPU Halt; active=0");
$display("Output:");
$display("%d",register_v0);
$finish;
end
endmodule