Commit 270c61e6 authored by Tristan Gingold's avatar Tristan Gingold

rtl: add ecc support (WIP)

parent 4a459e87
......@@ -11,5 +11,6 @@ files = [ "urv_cpu.v",
"urv_timer.v",
"urv_exceptions.v",
"urv_iram.v",
"urv_ecc.v",
"xurv_core.vhd",
"urv_pkg.vhd" ]
......@@ -36,6 +36,7 @@ module urv_cpu
parameter g_with_hw_mulh = 1, // Force mulh if mul is > 0 (for compatibility).
parameter g_with_hw_mul = 1, // 0: no multiply, 1: 32x32 mul. 2: mulh
parameter g_with_hw_debug = 0,
parameter g_with_ecc = 0,
parameter g_with_compressed_insns = 0
)
(
......@@ -109,6 +110,7 @@ module urv_cpu
// X2/W->RF interface
wire [4:0] rf_rd;
wire [31:0] rf_rd_value;
wire [6:0] rf_rd_ecc;
wire rf_rd_write;
// D->X1 stage interface
......@@ -121,6 +123,7 @@ module urv_cpu
wire [4:0] d2x_opcode;
wire d2x_shifter_sign;
wire d2x_is_load, d2x_is_store, d2x_is_undef;
wire d2x_is_write_ecc;
wire [31:0] d2x_imm;
wire d2x_is_signed_alu_op;
wire d2x_is_add_o;
......@@ -146,9 +149,11 @@ module urv_cpu
wire x2w_load;
wire [1:0] x2w_rd_source;
wire x2w_valid;
wire x2w_ecc_flip;
// Register file signals
wire [31:0] x_rs2_value, x_rs1_value;
wire x_rs1_ecc_err, x_rs2_ecc_err;
wire [31:0] rf_bypass_rd_value = x2w_rd_value;
wire rf_bypass_rd_write = rf_rd_write && !x2w_load; // multiply/shift too?
......@@ -236,6 +241,7 @@ module urv_cpu
.x_is_load_o(d2x_is_load),
.x_is_store_o(d2x_is_store),
.x_is_undef_o(d2x_is_undef),
.x_is_write_ecc_o(d2x_is_write_ecc),
.x_is_multiply_o(d2x_is_multiply),
.x_is_divide_o(d2x_is_divide),
.x_rd_source_o(d2x_rd_source),
......@@ -252,7 +258,9 @@ module urv_cpu
);
// Register File (RF)
urv_regfile regfile
urv_regfile
#(.g_with_ecc(g_with_ecc))
regfile
(
.clk_i(clk_i),
.rst_i(rst_i),
......@@ -267,9 +275,12 @@ module urv_cpu
.x_rs1_value_o(x_rs1_value),
.x_rs2_value_o(x_rs2_value),
.x_rs1_ecc_err_o(x_rs1_ecc_err),
.x_rs2_ecc_err_o(x_rs2_ecc_err),
.w_rd_i(rf_rd),
.w_rd_value_i(rf_rd_value),
.w_rd_ecc_i(rf_rd_ecc),
.w_rd_store_i(rf_rd_write),
.w_bypass_rd_write_i(rf_bypass_rd_write),
......@@ -298,6 +309,8 @@ module urv_cpu
// from register file
.rf_rs1_value_i(x_rs1_value),
.rf_rs2_value_i(x_rs2_value),
.rf_rs1_ecc_err_i(x_rs1_ecc_err),
.rf_rs2_ecc_err_i(x_rs2_ecc_err),
// from D stage
.d_valid_i(d2x_valid),
......@@ -316,6 +329,7 @@ module urv_cpu
.d_is_load_i(d2x_is_load),
.d_is_store_i(d2x_is_store),
.d_is_undef_i(d2x_is_undef),
.d_is_write_ecc_i(d2x_is_write_ecc),
.d_is_multiply_i(d2x_is_multiply),
.d_is_divide_i(d2x_is_divide),
.d_alu_op1_i(d2x_alu_op1),
......@@ -346,6 +360,7 @@ module urv_cpu
.w_rd_source_o(x2w_rd_source),
.w_rd_shifter_o(x2w_rd_shifter),
.w_rd_multiply_o(x2w_rd_multiply),
.w_ecc_flip_o(x2w_ecc_flip),
// Data memory I/F
.dm_addr_o(dm_addr_o),
......@@ -366,7 +381,9 @@ module urv_cpu
);
// Execute 2/Writeback stage
urv_writeback writeback
urv_writeback
#(.g_with_ecc(g_with_ecc))
writeback
(
.clk_i(clk_i),
.rst_i(rst_i),
......@@ -386,6 +403,7 @@ module urv_cpu
.x_shifter_rd_value_i(x2w_rd_shifter),
.x_multiply_rd_value_i(x2w_rd_multiply),
.x_dm_addr_i(x2w_dm_addr),
.x_ecc_flip_i(x2w_ecc_flip),
// Data memory I/F
.dm_data_l_i(dm_data_l_i),
......@@ -394,6 +412,7 @@ module urv_cpu
// to register file
.rf_rd_value_o(rf_rd_value),
.rf_rd_ecc_o(rf_rd_ecc),
.rf_rd_o(rf_rd),
.rf_rd_write_o(rf_rd_write)
);
......
......@@ -62,6 +62,7 @@ module urv_decode
output reg x_is_load_o,
output reg x_is_store_o,
output reg x_is_undef_o,
output reg x_is_write_ecc_o,
output reg [2:0] x_rd_source_o,
output x_rd_write_o,
output reg [11:0] x_csr_sel_o,
......@@ -305,6 +306,8 @@ module urv_decode
x_is_mul <= d_is_mul && g_with_hw_mul;
x_is_write_ecc_o <= d_opcode == `OPC_CUST2 && d_fun == `FUNC_WRECC;
case (d_opcode)
`OPC_BRANCH:
x_is_signed_alu_op_o <= (d_fun == `BRA_GE || d_fun == `BRA_LT);
......@@ -369,6 +372,7 @@ module urv_decode
x_rd_source_o <= `RD_SOURCE_CSR;
else if (d_opcode == `OPC_OP && f_ir_i[25])
begin
// mul/div
if( !d_fun[2] )
begin
if( d_fun == `FUNC_MUL )
......@@ -389,6 +393,8 @@ module urv_decode
x_rd_write <= d_rd_nonzero;
`OPC_SYSTEM:
x_rd_write <= d_rd_nonzero && (d_fun != 0); // CSR instructions write to RD
`OPC_CUST2:
x_rd_write <= 1'b1;
default:
x_rd_write <= 0;
endcase // case (d_opcode)
......
......@@ -82,6 +82,10 @@
`define CSR_OP_CSRRSI 3'b110
`define CSR_OP_CSRRCI 3'b111
// funct3 for OPC_CUST2
// (they use shifter functions)
`define FUNC_WRECC `FUNC_SL
`define FUNC_FIXECC `FUNC_SR
`define RD_SOURCE_ALU 3'b000
`define RD_SOURCE_SHIFTER 3'b010
......
/*
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- uRV - a tiny and dumb RISC-V core
-- https://www.ohwr.org/projects/urv-core
--------------------------------------------------------------------------------
--
-- unit name: urv_ecc
--
-- description: uRV CPU: compute ecc
--
--------------------------------------------------------------------------------
-- Copyright CERN 2022
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------
*/
`timescale 1ns/1ps
module urv_ecc
(
input [31:0] dat_i,
output [6:0] ecc_o
);
assign ecc_o[0] = ^(dat_i & 32'b11000001010010000100000011111111);
assign ecc_o[1] = ^(dat_i & 32'b00100001001001001111111110010000);
assign ecc_o[2] = ^(dat_i & 32'b01101100111111110000100000001000);
assign ecc_o[3] = ^(dat_i & 32'b11111111000000011010010001000100);
assign ecc_o[4] = ^(dat_i & 32'b00010110111100001001001010100110);
assign ecc_o[5] = ^(dat_i & 32'b00010000000111110111000101100001);
assign ecc_o[6] = ^(dat_i & 32'b10001010100000100000111100011011);
endmodule // urv_ecc
......@@ -45,7 +45,8 @@ module urv_exec
input [31:0] rf_rs1_value_i,
input [31:0] rf_rs2_value_i,
input rf_rs1_ecc_err_i,
input rf_rs2_ecc_err_i,
input d_valid_i,
......@@ -67,6 +68,7 @@ module urv_exec
input d_is_divide_i,
input d_is_multiply_i,
input d_is_undef_i,
input d_is_write_ecc_i,
input [31:0] d_alu_op1_i,
input [31:0] d_alu_op2_i,
......@@ -99,6 +101,7 @@ module urv_exec
output reg [1:0] w_rd_source_o,
output [31:0] w_rd_shifter_o,
output [31:0] w_rd_multiply_o,
output reg w_ecc_flip_o,
// Data memory I/F (address/store)
......@@ -124,7 +127,6 @@ module urv_exec
// Use rs1 and rs2, it's shorter; but keep long name for the ports.
wire [31:0] rs1, rs2;
assign rs1 = rf_rs1_value_i;
assign rs2 = rf_rs2_value_i;
......@@ -274,6 +276,8 @@ module urv_exec
`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_FIXECC: alu_result <= rf_rs1_ecc_err_i ? alu_op2 : alu_op1;
`FUNC_WRECC: alu_result <= alu_op1;
`FUNC_SLT,
`FUNC_SLTU: alu_result <= {31'b0, alu_sub[32]};
default: alu_result <= 32'hx;
......@@ -398,6 +402,14 @@ module urv_exec
x_interrupt <= 0;
x_exception_cause <= `CAUSE_ILLEGAL_INSN;
end
else if ((d_use_rs1_i && rf_rs1_ecc_err_i)
|| (d_use_rs2_i && rf_rs2_ecc_err_i))
begin
// Ecc error
x_exception <= 1;
x_interrupt <= 0;
x_exception_cause <= `CAUSE_ECC_ERROR;
end
else if (unaligned_addr
&& (d_opcode_i == `OPC_LOAD || d_opcode_i == `OPC_STORE))
begin
......@@ -497,6 +509,7 @@ module urv_exec
f_dbg_toggle_o <= 0;
w_load_o <= 0;
w_store_o <= 0;
w_ecc_flip_o <= 0;
// Values so that 0 could be written to register 0.
w_rd_value_o <= 0;
w_rd_o <= 0;
......@@ -514,6 +527,7 @@ module urv_exec
f_branch_target_o <= branch_target;
w_rd_o <= d_rd_i;
w_rd_value_o <= rd_value;
w_ecc_flip_o <= d_is_write_ecc_i & rs2[0];
f_branch_take <= branch_take && !x_kill_i && d_valid_i;
f_dbg_toggle_o <= g_with_hw_debug && d_is_ebreak_i && !x_kill_i && d_valid_i;
......
......@@ -29,19 +29,22 @@
`timescale 1ns/1ps
module urv_regmem
#(
parameter g_width = 32
)
(
input clk_i,
input clk_i,
input en1_i,
input [4:0] a1_i,
output reg [31:0] q1_o,
input en1_i,
input [4:0] a1_i,
output reg [g_width-1:0] q1_o,
input [4:0] a2_i,
input [31:0] d2_i,
input we2_i
input [4:0] a2_i,
input [g_width-1:0] d2_i,
input we2_i
);
reg [31:0] ram [0:31];
reg [g_width-1:0] ram [0:31];
always@(posedge clk_i)
if(en1_i)
......@@ -64,39 +67,55 @@ module urv_regmem
endmodule
module urv_regfile
(
input clk_i,
input rst_i,
input d_stall_i,
input [4:0] rf_rs1_i,
input [4:0] rf_rs2_i,
#(
parameter g_with_ecc = 0
)
(
input clk_i,
input rst_i,
input d_stall_i,
input [4:0] d_rs1_i,
input [4:0] d_rs2_i,
input [4:0] rf_rs1_i,
input [4:0] rf_rs2_i,
output reg [31:0] x_rs1_value_o,
output reg [31:0] x_rs2_value_o,
input [4:0] d_rs1_i,
input [4:0] d_rs2_i,
input [4:0] w_rd_i,
input [31:0] w_rd_value_i,
input w_rd_store_i,
output reg [31:0] x_rs1_value_o,
output reg [31:0] x_rs2_value_o,
output reg x_rs1_ecc_err_o,
output reg x_rs2_ecc_err_o,
input w_bypass_rd_write_i,
input [31:0] w_bypass_rd_value_i
input [4:0] w_rd_i,
input [31:0] w_rd_value_i,
input [6:0] w_rd_ecc_i,
input w_rd_store_i,
input w_bypass_rd_write_i,
input [31:0] w_bypass_rd_value_i
);
localparam g_width = 32 + (g_with_ecc ? 7 : 0);
wire [31:0] rs1_regfile;
wire [31:0] rs2_regfile;
wire [g_width-1:0] rs1_regfile;
wire [g_width-1:0] rs2_regfile;
// By adding rst_i, register 0 is written (to 0) during reset. This is
// required on some flash FPGA (like smartfusion2 or ProASIC3) which
// doesn't support initialized RAMs.
wire write = rst_i || (w_rd_store_i && (w_rd_i != 0));
urv_regmem bank0
wire rs1_ecc_err;
wire rs2_ecc_err;
// Value to be written in the register file
wire [g_width-1:0] w_rd_value;
assign w_rd_value = g_with_ecc ? {w_rd_ecc_i, w_rd_value_i} : w_rd_value_i;
urv_regmem
#(.g_width(g_width))
bank0
(
.clk_i(clk_i),
.en1_i(!d_stall_i),
......@@ -104,11 +123,12 @@ module urv_regfile
.q1_o(rs1_regfile),
.a2_i(w_rd_i),
.d2_i(w_rd_value_i),
.d2_i(w_rd_value),
.we2_i (write));
urv_regmem bank1
urv_regmem
#(.g_width(g_width))
bank1
(
.clk_i(clk_i),
.en1_i(!d_stall_i),
......@@ -116,14 +136,34 @@ module urv_regfile
.q1_o(rs2_regfile),
.a2_i (w_rd_i),
.d2_i (w_rd_value_i),
.d2_i (w_rd_value),
.we2_i (write)
);
wire rs1_bypass_x = w_bypass_rd_write_i && (w_rd_i == d_rs1_i) && (w_rd_i != 0);
wire rs2_bypass_x = w_bypass_rd_write_i && (w_rd_i == d_rs2_i) && (w_rd_i != 0);
generate
if (g_with_ecc) begin
wire [6:0] rs1_ecc;
wire [6:0] rs2_ecc;
urv_ecc ecc_rs1
(.dat_i(rs1_regfile[31:0]),
.ecc_o(rs1_ecc));
urv_ecc ecc_rs2
(.dat_i(rs2_regfile[31:0]),
.ecc_o(rs2_ecc));
assign rs1_ecc_err = |(rs1_ecc ^ rs1_regfile[38:32]);
assign rs2_ecc_err = |(rs2_ecc ^ rs2_regfile[38:32]);
end
else begin
assign rs1_ecc_err = 1'b0;
assign rs2_ecc_err = 1'b0;
end
endgenerate
wire rs1_bypass_x = w_bypass_rd_write_i && (w_rd_i == d_rs1_i) && (w_rd_i != 0);
wire rs2_bypass_x = w_bypass_rd_write_i && (w_rd_i == d_rs2_i) && (w_rd_i != 0);
reg rs1_bypass_w, rs2_bypass_w;
reg rs1_bypass_w, rs2_bypass_w;
always@(posedge clk_i)
if(rst_i)
......@@ -145,20 +185,38 @@ module urv_regfile
begin
case ( {rs1_bypass_x, rs1_bypass_w } ) // synthesis parallel_case full_case
2'b10, 2'b11:
x_rs1_value_o <= w_bypass_rd_value_i;
begin
x_rs1_value_o <= w_bypass_rd_value_i;
x_rs1_ecc_err_o <= 1'b0;
end
2'b01:
x_rs1_value_o <= bypass_w;
begin
x_rs1_value_o <= bypass_w;
x_rs1_ecc_err_o <= 1'b0;
end
default:
x_rs1_value_o <= rs1_regfile;
begin
x_rs1_value_o <= rs1_regfile[31:0];
x_rs1_ecc_err_o <= rs1_ecc_err;
end
endcase // case ( {rs1_bypass_x, rs1_bypass_w } )
case ( {rs2_bypass_x, rs2_bypass_w } ) // synthesis parallel_case full_case
2'b10, 2'b11:
x_rs2_value_o <= w_bypass_rd_value_i;
begin
x_rs2_value_o <= w_bypass_rd_value_i;
x_rs2_ecc_err_o <= 1'b0;
end
2'b01:
x_rs2_value_o <= bypass_w;
begin
x_rs2_value_o <= bypass_w;
x_rs2_ecc_err_o <= 1'b0;
end
default:
x_rs2_value_o <= rs2_regfile;
begin
x_rs2_value_o <= rs2_regfile[31:0];
x_rs2_ecc_err_o <= rs2_ecc_err;
end
endcase // case ( {rs2_bypass_x, rs2_bypass_w } )
end // always@ *
......
......@@ -29,34 +29,39 @@
`timescale 1ns/1ps
module urv_writeback
#(
parameter g_with_ecc = 0
)
(
input clk_i,
input rst_i,
output w_stall_req_o,
input [2:0] x_fun_i,
input x_load_i,
input x_store_i,
input [31:0] x_dm_addr_i,
input [4:0] x_rd_i,
input [31:0] x_rd_value_i,
input x_rd_write_i,
input x_valid_i,
input [31:0] x_shifter_rd_value_i,
input [31:0] x_multiply_rd_value_i,
input [1:0] x_rd_source_i,
input [31:0] dm_data_l_i,
input dm_load_done_i,
input dm_store_done_i,
output [31:0] rf_rd_value_o,
output [4:0] rf_rd_o,
output rf_rd_write_o
input clk_i,
input rst_i,
output w_stall_req_o,
input [2:0] x_fun_i,
input x_load_i,
input x_store_i,
input [31:0] x_dm_addr_i,
input [4:0] x_rd_i,
input [31:0] x_rd_value_i,
input x_rd_write_i,
input x_valid_i,
input [31:0] x_shifter_rd_value_i,
input [31:0] x_multiply_rd_value_i,
input [1:0] x_rd_source_i,
input x_ecc_flip_i,
input [31:0] dm_data_l_i,
input dm_load_done_i,
input dm_store_done_i,
output [31:0] rf_rd_value_o,
output [4:0] rf_rd_o,
output [6:0] rf_rd_ecc_o,
output rf_rd_write_o
);
reg [31:0] load_value;
......@@ -129,6 +134,19 @@ module urv_writeback
$error("Attempt to write unknown value to reg %x", x_rd_i);
// synthesis translate_on
generate
if (g_with_ecc) begin
wire [6:0] rf_rd_ecc;
urv_ecc gen_ecc
(.dat_i(rf_rd_value),
.ecc_o(rf_rd_ecc));
assign rf_rd_ecc_o = rf_rd_ecc ^ x_ecc_flip_i;
end
else
assign rf_rd_ecc_o = 6'bx;
endgenerate
assign rf_rd_write_o = rf_rd_write;
assign rf_rd_value_o = rf_rd_value;
assign rf_rd_o = x_rd_i;
......
......@@ -302,6 +302,14 @@ module main;
endcase // case (csr)
endfunction // decode_csr
function string decode_cust2(bit[2:0] fun);
case(fun)
`FUNC_WRECC: return "wecc";
`FUNC_FIXECC: return "fecc";
default: return "???";
endcase
endfunction
task automatic verify_branch(input [31:0] rs1, input[31:0] rs2, input take, input [2:0] fun);
int do_take;
......@@ -489,7 +497,8 @@ module main;
`OPC_CUST2:
begin
opc = "cust2";
fun = $sformatf("%03b", DUT.d2x_fun);
fun = decode_cust2(DUT.d2x_fun);
// fun = $sformatf("%03b", DUT.d2x_fun);
args = $sformatf("%-4s %-4s %-4s", rd, rs1, rs2);
end
default:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment