存取控制模块:再进化
在 从零开始学RISC:第十篇 中,设计了LoadStoreUnit来处理各类存取指令,但将同一个模块复用了两遍。这样会带来额外的逻辑开销。
最好的办法是将其拆分为两个模块:一个放在MEM级,负责存储;另一个放在WB级,负责读取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
module StoreUnit ( input logic [ 3:0] sl_type, input logic [31:0] addr, input logic [31:0] store_data_i, output logic [31:0] store_data_o, input logic dram_we, output logic [ 3:0] wstrb );
always_comb begin wstrb = 4'b0000; store_data_o = 32'b0;
if (dram_we) begin unique case (sl_type[1:0]) 2'b01: begin logic [4:0] shift; shift = {addr[1:0], 3'b000};
unique case (addr[1:0]) 2'b00: wstrb = 4'b0001; 2'b01: wstrb = 4'b0010; 2'b10: wstrb = 4'b0100; 2'b11: wstrb = 4'b1000; default: wstrb = 4'b0000; endcase
store_data_o = ({24'b0, store_data_i[7:0]} << shift); end
2'b10: begin logic [4:0] shift; shift = {addr[1], 4'b0000};
unique case (addr[1]) 1'b0: wstrb = 4'b0011; 1'b1: wstrb = 4'b1100; default: wstrb = 4'b0000; endcase
store_data_o = ({16'b0, store_data_i[15:0]} << shift); end
2'b11: begin wstrb = 4'b1111; store_data_o = store_data_i; end
default: begin wstrb = 4'b0000; store_data_o = 32'b0; end endcase end end
`ifdef DEBUG logic [31:0] sl_type_ascii; always_comb begin case (sl_type) `MEM_SB: sl_type_ascii = "SB "; `MEM_SH: sl_type_ascii = "SH "; `MEM_SW: sl_type_ascii = "SW "; default: sl_type_ascii = "----"; endcase end `endif
endmodule
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
module LoadUnit ( input logic [ 3:0] sl_type, input logic [31:0] addr, input logic [31:0] load_data_i, output logic [31:0] load_data_o );
logic is_load_unsigned; assign is_load_unsigned = (sl_type[2] == 1'b1);
always_comb begin logic [31:0] raw; raw = 32'b0; load_data_o = 32'b0;
case (sl_type[1:0]) 2'b01: begin raw = (load_data_i >> (addr[1:0] * 8)) & 32'h000000FF; end 2'b10: begin raw = (load_data_i >> (addr[1] * 16)) & 32'h0000FFFF; end 2'b11: begin raw = load_data_i; end default: raw = 32'b0; endcase
if (is_load_unsigned) begin load_data_o = raw; end else begin case (sl_type[1:0]) 2'b01: load_data_o = {{24{raw[7]}}, raw[7:0]}; 2'b10: load_data_o = {{16{raw[15]}}, raw[15:0]}; 2'b11: load_data_o = raw; default: load_data_o = 32'b0; endcase end end
`ifdef DEBUG logic [31:0] sl_type_ascii; logic [ 1:0] select_bits; assign select_bits = addr[1:0]; always_comb begin case (sl_type) `MEM_LB: sl_type_ascii = "LB "; `MEM_LH: sl_type_ascii = "LH "; `MEM_LW: sl_type_ascii = "LW "; `MEM_LBU: sl_type_ascii = "LBU "; `MEM_LHU: sl_type_ascii = "LHU "; default: sl_type_ascii = "----"; endcase end `endif
endmodule
|
拆分后的逻辑就清楚多了。