CSR模块
387 cpu7_csr csr(
388 .clk (clk ),
389 .resetn (resetn ),
390 .csr_rdata (csr_byp_rdata_d ),
391 .csr_raddr (ecl_csr_raddr_d ),
392 .csr_waddr (ecl_csr_waddr_m ),
393 .csr_wdata (byp_csr_wdata_m ),
394 .csr_wen (ecl_csr_wen_m )
395 );
在_d的时候读csr寄存器的内容。
处理csrwr csrxchg的时候需要读rd的内容,写到csr寄存器里。因为读到的rd的内容需要经过byp处理,所以在_e写csr寄存器来不及,只能在_m写。
695 assign csr_wdata_e = byp_rs2_data_e;
并且_e的时候还要处理csrxchg里的mask。
但这样就出现个问题,csr寄存器也需要byp。
比如csrwr后面紧跟着csrrd,csrwr在_m时写csr寄存器,而csrrd在_d的时候就去读了,比如下面这段代码。
addi.w $r4, $r0, -1 # r4 = ffffffff
csrwr $r4, 0x0 # only crmd.ie (bit 2) can be set
# nop
csrrd $r5, 0x0 # crmd
addi.w $r5, $r5, 0x56
没有nop隔开的话,csrrd读到的还是0。
csr寄存器还不能直接做bypass,因为很多寄存器里很多bit都相当于是只读的0。
比如csrwr刚写ffffffff到crmd,而crmd现在只有ie位有寄存器实现,所以ffffffff写进crmd,再读出来的时候就变成00000004了。
如果后面的csrrd读的时候做data forwarding,则csrrd得到的是csrwr的ffffffff,这样结果就不对了。
具体的写在这里了:
现在的办法是和lsu一样,request IFU stall。
因为这是个RAW,Read-After-Write,所以只有csrrw csrxchg需要stall IFU。
按说只需要在csrrw csrxchg后面是csrrd,并且只有csr_num一致才需要把csrrd这条指令stall一下。
不过实现这样判断有点复杂,不过又不能不管,因为结果必须正确。
csr指令用的并不多,所以就csrwr csrxchg就stall IFU吧。
stall IFU
794 //
795 // csr stall req
796
797 wire csr_stall_req;
798 wire csr_stall_req_next;
799
800 assign csr_stall_req_next = (csr_wen_d) | (csr_stall_req & ~csr_wen_m);
801
802 dffr_s #(1) csr_stall_req_reg (
803 .din (csr_stall_req_next),
804 .clk (clk),
805 .q (csr_stall_req),
806 .se(), .si(), .so(), .rst (~resetn));
csr_wen_d是stall开始的信号,csr_wen_m是stall结束的信号。
和lsu用的方法一样,设置开始和结束的信号,上面的逻辑 csr_stall_req_next就能保持1在这两个信号中间。
最后用这个信号去req IFU stall。
1009 //
1010 // exu_ifu_stall_req
1011 //
1012 assign exu_ifu_stall_req = lsu_stall_req_next | csr_stall_req_next;
用csr_stall_req_next还是csr_stall_req,在起始位值上略有不同。
为了一遇到csrwr指令就尽快stall住IFU,用csr_stall_req_next。
因为差一个cycle的话,IFU的下一条指令就进来了。
又加了个测试例子 func_uty14_csrwr1cycle,测下一个cycle一个指令的时候行不行。
obj/main.elf: file format elf32-loongarch
obj/main.elf
Disassembly of section .text:
1c000000 <_start>:
kernel_entry():
1c000000: 02800803 addi.w $r3,$r0,2(0x2)
1c000004: 02800004 addi.w $r4,$r0,0
1c000008 <again>:
again():
1c000008: 02800484 addi.w $r4,$r4,1(0x1)
1c00000c: 02bffc06 addi.w $r6,$r0,-1(0xfff)
1c000010: 04000026 csrwr $r6,0x0
1c000014: 04000005 csrrd $r5,0x0
1c000018: 028158a5 addi.w $r5,$r5,86(0x56)
1c00001c: 02800000 addi.w $r0,$r0,0
1c000020: 02800000 addi.w $r0,$r0,0
1c000024: 5fffe464 bne $r3,$r4,-28(0x3ffe4) # 1c000008 <again>
黄色框里pc_bf由于exu_ifu_stall_req的控制,一直停在1c000014,这段和下面csr_stall_req_next是一致的。
红色框里pc_f也一直停在1c000014。
76 assign fdp_dec_valid = inst_valid & ~exu_ifu_stall_req & ~br_taken; // pc_f shoudl not be passed to pc_d if a branch is taken at _e.
135 wire pc_f2d_en = fdp_dec_valid;
136
137 dffe_s #(`GRLEN) pc_f2d_reg (
138 .din (pc_f),
139 .clk (clock),
140 .q (pc_d),
141 .en (pc_f2d_en),
142 .se(), .si(), .so());
195 assign ifu_pcbf_sel_old_bf_l = (inst_valid || reset || br_taken) & (~exu_ifu_stall_req);
196
197 //assign ifu_pcbf_sel_pcinc_bf_l = ~(inst_valid && ~br_taken); /// ??? br_taken never comes along with inst_valid, br_taken_e
198 assign ifu_pcbf_sel_pcinc_bf_l = ~(inst_valid && ~br_taken) | exu_ifu_stall_req; /// ??? br_taken never comes along with inst_valid, br_\
taken_e
exu_ifu_stall_req影响fdp_dec_valid,这样pc_f的指令不会走到后面的_d阶段。