u@home:~$

Address misalign exception

想开始加exception了,之前cpu6 soc1里面只做过illegal instruction的,这次准备先搞address misalign的。

lsu里没处理这个,比如store,不管地址是不是misalign,都直接发出去给cache,发出data_req,只是要写的数据没有写进去。

screenshot0

st.h存两个字节,地址是0xc0000019,是不对其的。data_addr_ok是给回来了,后面data_data_ok是咋回事,这是个st指令。。

先记下,回头再调。

看了下龙芯手册,不是用的中断向量表,除了TLB,所有的exception和interrupt都是走CSR.EENTRY的入口。

然后通过CSR.ESTA里的信息由软件来判断是什么情况。

单这个EENTRY地址怎么定的,手册里没有。reset后的地址是1c000000是手册给出来的,这个EENTRY没给。

看了下chiplab,chiplab里没有exception这部分功能,之前也没注意。

查了下龙芯手册,结果发现LoongArch32和LoongArch64还不一样。

screenshot1

screenshot2

或者说LoongArch32是CSR.ECFG.VS为0时候的特殊情况。LoongArch64里可以用中断向量表的,为什么要这么设计,还不懂。

screenshot3


调的过程中遇到个问题。

lsu里给出的lsu_rdata_valid_m,这个名字我起的不好,应该叫lsu_finish_m。表示load或store操作结束,但并不能保证store是否成功,load是否成功读到数据。

问题是,如果lsu_rdata_valid_m不给出,lsu就一直stall ifu。

assign lsu_rdata_valid_m = data_data_ok;
 1015    //                                                                                                                                       
 1016    // lsu_dispatch_d is the staring signal                                                                                                  
 1017    // lsu_ecl_rdata_valid_m ends it                                                                                                         
 1018    //                                                                                                                                       
 1019    assign lsu_stall_req_next =  (lsu_dispatch_d) | (lsu_stall_req & ~lsu_ecl_rdata_valid_m);                                                
 1020                                                                                                                                             
 1021    dffr_s #(1) lsu_stall_req_reg (                                                                                                          
 1022       .din (lsu_stall_req_next),                                                                                                            
 1023       .clk (clk),                                                                                                                           
 1024       .q   (lsu_stall_req),                                                                                                                 
 1025       .se(), .si(), .so(), .rst (~resetn));

导致的问题就是即使store的时候有address misalign exception,因为ifu被stall住了,pc_bf接换到eentry,但没法取值,就全卡在那了。

screenshot4

之前改成了如果有exception就不发data_req,这样后面应该自己给出lsu_finish_m,让这条store指令结束,直接进eentry。

assign data_req      = valid & !lsu_except; 

这样还要等一拍再给lsu_finish_m,也就是lsu_rdata_valid_m,因为ale是在_e就发现的。

现在就先让data_req发出,就算有exception也发出,让外面cache按原来的逻辑走,以后再改这块。

改了以后, lsu_rdata_valid_m就下个周期就回来了,这样store指令就能完成(红框),这样才能跳到eentry继续后面的代码。

screenshot5

不过黄框里的lsu_except却一直是1,这里应该和lsu的valid一起给出信号,表示这个except确实是lsu这时候给出的。

lsu_except一直是1也会让pc_bf一直卡在eentry上。

lsu_dispatch_d是在_d时的,在_e的dispatch,就是lsu里的valid。

 330    // ale should go with valid, and valid is actually lsu_dispatch_e, only last one cycle                                                    
 331    assign lsu_ecl_ale_e = lsu_ale & valid;

screenshot6

现在的问题是exu_ifu_stall_req是2cycle,而lsu_except是1个cycle,这两个信号会同时影响pc_bf。

最好的情况是exu_ifu_stall_req后马上接lsu_except,意思是ifu不被当前store指令stall了以后,马上跳入eentry。

首先,

 206    assign ifu_pcbf_sel_excpc_bf_l = ~exu_ifu_except | exu_ifu_stall_req; 

ifu_pcbf_sel_excpc_bf_l不能这样,因为exu_ifu_stall_req比exu_ifu_except持续的久,所以永远ifu_pcbf_sel_excpc_bf_l也不会是0。

而且exu_ifu_except的优先级要高过exu_ifu_stall_req,这个意思是exu_ifu_stall_req的过程中(可能是load或store指令引起的)可能会发生异常。

异常出了以后ifu取值的pc_bf要马上指向eentry,这个时候exu_ifu_stall_req应该取消了,因为load或store指令出错,已经被终止了。

取消exu_ifu_stall_req要到源头上去取消,有可能是lsu,也可能是csr,就麻烦了。

可以让exu_ifu_except的优先级高,这样取值pc_bf就没问题。但隐藏的问题可能是,当这个cycle过了,stall还在继续,特别是load指令,这个stall可能还要持续4-5个cycle。

所以最正确的做法是,在lsu源头上,_e的时候就能发现ale这种问题,就不data_req了,而是让

assign lsu_rdata_valid_m = data_data_ok;

变成

assign lsu_rdata_valid_m = data_data_ok | lsu_except;

而这个信号的名字也应该改成

assign lsu_finish_m = data_data_ok | lsu_except;

不过这个信号是在_m给出的,这就还是遇到了开头说到的那个问题。

并且stall还是会持续同样的2个cycle。


这样不管怎么改都很乱啊,跳转的逻辑很复杂。

要不要考虑和中断一样,exception也是标记上,然后跟着指令走,在_w的时候处理?

这时候lsu都已经在_m的时候完成了指令。

这样也有问题,不是在第一时间,而是在_w的时候处理exception,这样前面进到pipeline里的指令要刷掉。

还是先看看opensparc。


先处理了exu_ifu_except,这样pc_bf选择地址是没问题了。

 200    assign ifu_pcbf_sel_old_bf_l = ((inst_valid || reset || br_taken) & (~exu_ifu_stall_req)) | exu_ifu_except; // exception need ifu to fetc\
     h instruction from eentry    

而且对于store指令来说,没啥问题。

但按之前说的,在load的时候,exu_ifu_except过去以后还会有很长的一段stall继续。

screenshot7


改成之前说的那样了,如果有ale,不发data_req,并且自己signal lsu_finish_m。

 292    // if ale, data req is not sent out, so there will be no data_data_ok back                                                                
 293    // lsu_ale is at _e, wait for the next cycle to signal lsu_finish_m                                                                       
 294    assign lsu_finish_m = data_data_ok | (lsu_ale_m & valid_m);                                                                               
 295                                                                                                                                              
 296                                                                                                                                              
 297                                                                                                                                              
 298    assign data_req      = valid_e & !lsu_ale_e; // if ale, do not send out data req 

screenshot8