Commit 7c857885 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

initial version of the core. prints 'hello world'

parents
sim_tool = "modelsim"
top_module="main"
syn_device="xc6slx150t"
action = "simulation"
target = "xilinx"
include_dirs=["."]
files = [ "main.sv",
"rv_cpu.v",
"rv_exec.v",
"rv_fetch.v",
"rv_predecode.v",
"rv_regfile.v",
"rv_writeback.v"];
/*
uRV - a tiny and dumb RISC-V core
Copyright (c) 2015 twl <twlostow@printf.cc>.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
*/
`include "rv_defs.v"
`timescale 1ns/1ps
module main;
reg clk = 0;
reg rst = 1;
wire [31:0] im_addr;
reg [31:0] im_data;
wire im_valid = 1'b1;
wire [31:0] dm_addr;
wire [31:0] dm_data_s;
reg [31:0] dm_data_l;
wire [3:0] dm_data_select;
wire dm_write;
localparam int mem_size = 16384;
reg [31:0] mem[0:mem_size - 1];
task automatic load_ram(string filename);
int f = $fopen(filename,"r");
int n, i;
while(!$feof(f))
begin
int addr, data;
string cmd;
$fscanf(f,"%s %08x %08x", cmd,addr,data);
if(cmd == "write")
begin
mem[addr % mem_size] = data;
end
end
endtask // load_ram
always@(posedge clk)
begin
im_data <= mem[(im_addr / 4) % mem_size];
if(dm_write && dm_data_select[0])
mem [(dm_addr / 4) % mem_size][7:0] <= dm_data_s[7:0];
if(dm_write && dm_data_select[1])
mem [(dm_addr / 4) % mem_size][15:8] <= dm_data_s[15:8];
if(dm_write && dm_data_select[2])
mem [(dm_addr / 4) % mem_size][23:16] <= dm_data_s[23:16];
if(dm_write && dm_data_select[3])
mem [(dm_addr / 4) % mem_size][31:24] <= dm_data_s[31:24];
dm_data_l <= mem[(dm_addr/4) % mem_size];
end
rv_cpu DUT
(
.clk_i(clk),
.rst_i(rst),
// instruction mem I/F
.im_addr_o(im_addr),
.im_data_i(im_data),
.im_valid_i(im_valid),
// data mem I/F
.dm_addr_o(dm_addr),
.dm_data_s_o(dm_data_s),
.dm_data_l_i(dm_data_l),
.dm_data_select_o(dm_data_select),
.dm_write_o(dm_write)
);
always #5ns clk <= ~clk;
initial begin
load_ram("sw/hello.ram");
repeat(3) @(posedge clk);
rst = 0;
end
function string decode_op(bit[2:0] fun);
case(fun)
`FUNC_ADD: return "add";
`FUNC_XOR:return "xor";
`FUNC_OR: return "or";
`FUNC_AND:return "and";
`FUNC_SLT:return "slt";
`FUNC_SLTU:return "sltu";
`FUNC_SL: return "sl";
`FUNC_SR:return"sr";
endcase // case (fun)
endfunction // decode_op
function string decode_cond(bit[2:0] fun);
case(fun)
`BRA_EQ: return "eq";
`BRA_NEQ: return "neq";
`BRA_LT: return "lt";
`BRA_GE: return "ge";
`BRA_LTU:return "ltu";
`BRA_GEU:return "geu";
endcase // case (fun)
endfunction // decode_op
function string decode_size(bit[2:0] fun);
case(fun)
`LDST_B: return "s8";
`LDST_BU: return "u8";
`LDST_H: return "s16";
`LDST_HU: return "u16";
`LDST_L:return "32";
endcase // case (fun)
endfunction // decode_op
function string decode_regname(bit[4:0] r);
case(r)
0: return "zero";
1: return "ra";
2: return "sp";
3: return "gp";
4: return "tp";
5: return "t0";
6: return "t1";
7: return "t2";
8: return "s0";
9: return "s1";
10: return "a0";
11: return "a1";
12: return "a2";
13: return "a3";
14: return "a4";
15: return "a5";
16: return "a6";
17: return "a7";
18: return "s2";
19: return "s3";
20: return "s4";
21: return "s5";
22: return "s6";
23: return "s7";
24: return "s8";
25: return "s9";
26: return "s10";
27: return "s11";
28: return "t3";
29: return "t4";
30: return "t5";
31: return "t6";
endcase // case (fun)
endfunction // decode_regname
function automatic string s_hex(int x);
return $sformatf("%s0x%-08x", x<0?"-":" ", (x<0)?(-x):x);
endfunction // s_hex
reg[31:0] dm_addr_d0;
always@(posedge clk)
begin
dm_addr_d0 <= dm_addr;
if(dm_write)
$display("DM Write addr %x data %x", dm_addr, dm_data_s);
if (DUT.writeback.x_load_i)
$display("DM Load addr %x data %x -> %s", dm_addr_d0, dm_data_l, decode_regname(DUT.writeback.x_rd_i));
end
always@(posedge clk)
if(dm_write && dm_addr == 'h100000)
$display("\n ****** TX '%c' \n", dm_data_s[7:0]) ;
always@(posedge clk)
if(!DUT.execute.x_stall_i && !DUT.execute.x_kill_i)
begin
automatic string opc="<unk>", fun="", args="";
automatic string rs1 = decode_regname(DUT.d2x_rs1);
automatic string rs2 = decode_regname(DUT.d2x_rs2);
automatic string rd = decode_regname(DUT.d2x_rd);
reg [31:0] imm;
// $display("Opcode %x", DUT.d2x_opcode);
case (DUT.d2x_opcode)
`OPC_AUIPC:
begin
opc = "auipc";
fun = "";
args = $sformatf("%-3s %-3s %s", rd, " ", s_hex(DUT.d2x_imm_u));
end
`OPC_LUI:
begin
opc = "lui";
fun = "";
args = $sformatf("%-3s %-3s %s", rd, " ", s_hex(DUT.d2x_imm_u));
end
`OPC_OP_IMM:
begin
opc = "op-imm";
fun = decode_op(DUT.d2x_fun);
args = $sformatf("%-3s %-3s %s", rd, rs1, s_hex(DUT.d2x_imm_i));
end
`OPC_OP:
begin
opc = "op-imm";
fun = decode_op(DUT.d2x_fun);
args = $sformatf("%-3s %-3s %-3s", rd, rs1, rs2);
end
`OPC_JAL:
begin
opc = "jal";
fun = "";
//decode_op(DUT.d2x_fun);
args = $sformatf("%-3s 0x%-08x", rd, DUT.execute.branch_target);
end
`OPC_JALR:
begin
opc = "jalr";
fun = "";
//decode_op(DUT.d2x_fun);
args = $sformatf("%-3s %-3s 0x%-08x", rd, rs1, DUT.execute.branch_target);
end
`OPC_BRANCH:
begin
opc = "branch";
fun = decode_cond(DUT.d2x_fun);
//decode_op(DUT.d2x_fun);
args = $sformatf("%-3s %-3s 0x%-08x %s", rs1, rs2, DUT.execute.branch_target, DUT.execute.branch_take?"TAKE":"IGNORE");
end
`OPC_LOAD:
begin
opc = "ld";
fun = decode_size(DUT.d2x_fun);
//decode_op(DUT.d2x_fun);
args = $sformatf("%-3s %-3s [0x%-08x + %s]", rd, rs1, DUT.execute.rs1, s_hex($signed(DUT.execute.d_imm_i_i)));
end
`OPC_STORE:
begin
opc = "st";
fun = decode_size(DUT.d2x_fun);
//decode_op(DUT.d2x_fun);
args = $sformatf("%-3s %-3s [0x%-08x + %s]", rs2, rs1, DUT.execute.rs1, s_hex($signed(DUT.execute.d_imm_s_i)));
end
endcase // case (d2x_opcode)
$display("%08x: %-8s %-3s %s", DUT.execute.d_pc_i, opc, fun, args);
end
endmodule // main
vlog -sv main.sv +incdir+. +incdir+../../include/wb +incdir+../../include/vme64x_bfm +incdir+../../include +incdir+../include +incdir+../../sim
vsim -t 1ps work.main -voptargs=+acc
set StdArithNoWarnings 1
set NumericStdNoWarnings 1
do wave.do
radix -hexadecimal
run 1.7us
\ No newline at end of file
/*
uRV - a tiny and dumb RISC-V core
Copyright (c) 2015 twl <twlostow@printf.cc>.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
*/
`include "rv_defs.v"
`timescale 1ns/1ps
module rv_cpu
(
input clk_i,
input rst_i,
// instruction mem I/F
output [31:0] im_addr_o,
input [31:0] im_data_i,
input im_valid_i,
// data mem I/F
output [31:0] dm_addr_o,
output [31:0] dm_data_s_o,
input [31:0] dm_data_l_i,
output [3:0] dm_data_select_o,
output dm_write_o
);
wire f_stall;
wire w_stall;
wire x_stall;
wire x_kill;
wire [31:0] f2d_pc, f2d_ir;
wire f2d_ir_valid;
wire [31:0] x2f_pc_bra;
wire x2f_bra;
rv_fetch fetch
(
.clk_i(clk_i),
.rst_i(rst_i),
.im_addr_o(im_addr_o),
.im_data_i(im_data_i),
.im_valid_i(im_valid_i),
.f_stall_i(f_stall),
.f_ir_o(f2d_ir),
.f_pc_o(f2d_pc),
.f_ir_valid_o(f2d_ir_valid),
.x_pc_bra_i(x2f_pc_bra),
.x_bra_i(x2f_bra)
);
wire [31:0] d2x_pc;
wire [4:0] rf_rs1, d2x_rs1;
wire [4:0] rf_rs2, d2x_rs2;
wire [4:0] d2x_rd;
wire [4:0] d2x_shamt;
wire [2:0] d2x_fun;
wire [4:0] d2x_opcode;
wire d2x_shifter_sign;
wire [31:0] d2x_imm_i, d2x_imm_s, d2x_imm_u, d2x_imm_b, d2x_imm_j;
rv_predecode decode
(
.clk_i(clk_i),
.rst_i(rst_i),
.im_data_i(im_data_i),
.f_ir_i(f2d_ir),
.f_pc_i(f2d_pc),
.x_pc_o(d2x_pc),
.rf_rs1_o(rf_rs1),
.rf_rs2_o(rf_rs2),
.x_rs1_o(d2x_rs1),
.x_rs2_o(d2x_rs2),
.x_rd_o(d2x_rd),
.x_shamt_o(d2x_shamt),
.x_fun_o(d2x_fun),
.x_opcode_o(d2x_opcode),
.x_shifter_sign_o(d2x_shifter_sign),
.x_imm_i_o(d2x_imm_i),
.x_imm_s_o(d2x_imm_s),
.x_imm_b_o(d2x_imm_b),
.x_imm_u_o(d2x_imm_u),
.x_imm_j_o(d2x_imm_j)
);
wire [31:0] x_rs2_value, x_rs1_value;
wire [4:0] rf_rd;
wire [31:0] rf_rd_value;
wire rf_rd_write;
rv_regfile regfile
(
.clk_i(clk_i),
.rst_i(rst_i),
.x_stall_i(x_stall),
.w_stall_i(w_stall),
.rf_rs1_i(rf_rs1),
.rf_rs2_i(rf_rs2),
.d_rs1_i(d2x_rs1),
.d_rs2_i(d2x_rs2),
.x_rs1_value_o(x_rs1_value),
.x_rs2_value_o(x_rs2_value),
.w_rd_i(rf_rd),
.w_rd_value_i(rf_rd_value),
.w_rd_store_i(rf_rd_write)
);
wire [4:0] x2w_rd;
wire [31:0] x2w_rd_value;
wire [31:0] x2w_dm_addr;
wire x2w_rd_write;
wire [2:0] x2w_fun;
rv_exec execute
(
.clk_i(clk_i),
.rst_i(rst_i),
.x_stall_i(x_stall),
.x_kill_i(x_kill),
.x_stall_req_o(x_stall_req),
.d_pc_i(d2x_pc),
.d_rd_i(d2x_rd),
.d_fun_i(d2x_fun),
.rf_rs1_value_i(x_rs1_value),
.rf_rs2_value_i(x_rs2_value),
.d_opcode_i(d2x_opcode),
.d_shifter_sign_i(d2x_shifter_sign),
.d_imm_i_i(d2x_imm_i),
.d_imm_s_i(d2x_imm_s),
.d_imm_b_i(d2x_imm_b),
.d_imm_u_i(d2x_imm_u),
.d_imm_j_i(d2x_imm_j),
.f_branch_target_o (x2f_pc_bra), // fixme: consistent naming
.f_branch_take_o (x2f_bra),
// Writeback stage I/F
.w_fun_o(x2w_fun),
.w_load_o(x2w_load),
.w_dm_addr_o(x2w_dm_addr),
.w_rd_o(x2w_rd),
.w_rd_value_o(x2w_rd_value),
.w_rd_write_o(x2w_rd_write),
.dm_addr_o(dm_addr_o),
.dm_data_s_o(dm_data_s_o),
.dm_data_select_o(dm_data_select_o),
.dm_write_o(dm_write_o)
);
rv_writeback writeback
(
.clk_i(clk_i),
.rst_i(rst_i),
.w_stall_i(w_stall),
.x_fun_i(x2w_fun),
.x_load_i(x2w_load),
.x_rd_i(x2w_rd),
.x_rd_value_i(x2w_rd_value),
.x_rd_write_i(x2w_rd_write),
.x_dm_addr_i(x2w_dm_addr),
.dm_data_l_i(dm_data_l_i),
.rf_rd_value_o(rf_rd_value),
.rf_rd_o(rf_rd),
.rf_rd_write_o(rf_rd_write)
);
reg x_bra_d0;
always@(posedge clk_i)
if(rst_i)
x_bra_d0 <= 0;
else if (!x_stall)
x_bra_d0 <= x2f_bra;
assign f_stall = 0;
assign x_stall = f_stall || (!f2d_ir_valid);
assign w_stall = x_stall;
assign x_kill = x2f_bra && ~x_bra_d0;
endmodule // rv_cpu
/*
uRV - a tiny and dumb RISC-V core
Copyright (c) 2015 twl <twlostow@printf.cc>.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
*/
`define OPC_OP_IMM 5'b00100
`define OPC_LUI 5'b01101
`define OPC_AUIPC 5'b00101
`define OPC_OP 5'b01100
`define OPC_JAL 5'b11011
`define OPC_JALR 5'b11001
`define OPC_BRANCH 5'b11000
`define OPC_LOAD 5'b00000
`define OPC_STORE 5'b01000
`define BRA_EQ 3'b000
`define BRA_NEQ 3'b001
`define BRA_LT 3'b100
`define BRA_GE 3'b100
`define BRA_LTU 3'b110
`define BRA_GEU 3'b111
`define LDST_B 3'b000
`define LDST_H 3'b001
`define LDST_L 3'b010
`define LDST_BU 3'b100
`define LDST_HU 3'b101
`define FUNC_ADD 3'b000
`define FUNC_SLT 3'b010
`define FUNC_SLTU 3'b011
`define FUNC_XOR 3'b100
`define FUNC_OR 3'b110
`define FUNC_AND 3'b111
`define FUNC_SL 3'b001
`define FUNC_SR 3'b101
/*
uRV - a tiny and dumb RISC-V core
Copyright (c) 2015 twl <twlostow@printf.cc>.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
*/
`include "rv_defs.v"
`timescale 1ns/1ps
module rv_exec
(
input clk_i,
input rst_i,
input x_stall_i,
input x_kill_i,
output reg x_stall_req_o,
input [31:0] d_pc_i,
input [4:0] d_rd_i,
input [2:0] d_fun_i,
input [31:0] rf_rs1_value_i,
input [31:0] rf_rs2_value_i,
input [4:0] d_opcode_i,
input d_shifter_sign_i,
input [31:0] d_imm_i_i,
input [31:0] d_imm_s_i,
input [31:0] d_imm_b_i,
input [31:0] d_imm_u_i,
input [31:0] d_imm_j_i,
output reg [31:0] f_branch_target_o,
output reg f_branch_take_o,
// Writeback stage I/F
output reg [2:0 ] w_fun_o,
output reg w_load_o,
output reg [4:0] w_rd_o,
output reg [31:0] w_rd_value_o,
output reg w_rd_write_o,
output reg [31:0] w_dm_addr_o,
// Data memory I/F (address/store)
output [31:0] dm_addr_o,
output [31:0] dm_data_s_o,
output [3:0] dm_data_select_o,
// input [31:0] dm_data_l_i,
output dm_write_o
);
wire [31:0] rs1, rs2;
assign rs1 = rf_rs1_value_i;
assign rs2 = rf_rs2_value_i;
reg [31:0] alu_op1, alu_op2, alu_result;
reg [31:0] rd_value;
reg branch_take;
reg branch_condition_met;
reg [31:0] branch_target;
reg [31:0] dm_addr, dm_data_s, dm_select_s;
reg dm_write_s;
reg rd_write;
// branch condition decoding
always@*
case (d_fun_i)
`BRA_EQ: branch_condition_met <= (rs1 == rs2);
`BRA_NEQ: branch_condition_met <= ~(rs1 == rs2);
`BRA_GEU: branch_condition_met <= ($signed(rs1) >= $signed(rs2));
`BRA_LTU: branch_condition_met <= ($signed(rs1) < $signed(rs2));
`BRA_GE: branch_condition_met <= (rs1 >= rs2);
`BRA_LT: branch_condition_met <= (rs1 < rs2);
default: branch_condition_met <= 0;
endcase // case (d_fun_i)
always@*
case (d_opcode_i)
`OPC_JAL: branch_target <= d_pc_i + d_imm_j_i;
`OPC_JALR: branch_target <= rs1 + d_imm_i_i;
`OPC_BRANCH: branch_target <= d_pc_i + d_imm_b_i;
default: branch_target<= 32'hx;
endcase // case (d_opcode_i)
// decode ALU operands
always@*
begin
alu_op1 <= rs1;
alu_op2 <= (d_opcode_i == `OPC_OP_IMM) ? d_imm_i_i : rs2;
end
// the ALU itself
always@*
begin
case (d_fun_i)
`FUNC_ADD: alu_result <= alu_op1 + alu_op2;
`FUNC_XOR: alu_result <= alu_op1 ^ alu_op2;
`FUNC_OR: alu_result <= alu_op1 | alu_op2;
`FUNC_AND: alu_result <= alu_op1 & alu_op2;
`FUNC_SLT: alu_result <= ($signed(alu_op1) < $signed(alu_op2)) ? 1 : 0;
`FUNC_SLTU: alu_result <= ((alu_op1) < (alu_op2)) ? 1 : 0;
`FUNC_SL: alu_result <= alu_op1 << alu_op2[4:0];