目录
- LoongArch 个人赛一级评测(前递旁路+load阻塞)
- 声明
- 测试说明
- 代码修改
- thinpad_top.v
- conver_ram.v
- IF_stage
- 测试
- 自动评测
- 在线实验
- 踩坑记录
- 读写使能控制
- inout类端口的使用方法
- Vivado生成Bit流文件时出现[Synth 8-91] ambiguous clock in event control
- RAM字节使能的使用
- | 运算
- 参考资料
LoongArch 个人赛一级评测(前递旁路+load阻塞)
声明
本文使用的是远程自主龙架构CPU设计实验平台,需要使用LoongArch工程模板生成bit流文件。
测试说明
本实验需要实现一个 32 位 LoongArch CPU,支持LoongArch-C1指令集的 6 条指令:addi.w lu12i.w add.w st.w ld.w bne。
自动化脚本会通过运行下面的汇编程序,对你的 CPU 进行正确性测试。
测试过程对CPU实现有如下要求:
- 虚拟内存空间为0x80000000~0x807FFFFF,共8MB,要求整个内存空间均可读可写可执行。其中
- 0x80000000~0x803FFFFF映射到BaseRAM;
- 0x80400000~0x807FFFFF映射到ExtRAM。
- CPU字节序为小端序。
- CPU时钟使用外部时钟输入,50MHz / 11MHz两路时钟输入均可使用。
- 在复位按钮按下时(高电平)CPU处于复位状态,松开后解除。
- CPU复位后从0x80000000开始取指令执行。
测试步骤
- 将BaseRAM和ExtRAM重置,并将测试程序汇编成机器语言后写入到BaseRAM中。
- 单击复位按钮。
- 此时你的 CPU 应当从 BaseRAM 的起始地址开始执行程序,程序功能为循环斐波那契程序计算64次,结果分别写在ExtRAM的0x0 ~ 0x100中。
- 等待 1s。
- 脚本读取 ExtRAM 中地址为 0x0 ~ 0x100 的数据,注意此处地址的单位与 CPU 中一致,均为字节。
代码修改
thinpad_top.v
wire my_clk;
wire my_reset;
assign my_clk = clk_10M;
assign my_reset = reset_of_clk10M;// inst ram <-> base ram
conver_ram inst_conver_ram(.clk (my_clk ),.resetn (my_reset ),.cpu_sram_en (cpu_inst_en ),.cpu_sram_we (cpu_inst_we ),.cpu_sram_addr (cpu_inst_addr ),.cpu_sram_wdata (cpu_inst_wdata),.cpu_sram_rdata (cpu_inst_rdata),.ram_data (base_ram_data ),.ram_addr (base_ram_addr ),.ram_be_n (base_ram_be_n ),.ram_ce_n (base_ram_ce_n ),.ram_oe_n (base_ram_oe_n ),.ram_we_n (base_ram_we_n )
);
// data ram <-> ext ram
conver_ram data_conver_ram(.clk (my_clk ),.resetn (my_reset ),.cpu_sram_en (cpu_data_en ),.cpu_sram_we (cpu_data_we ),.cpu_sram_addr (cpu_data_addr ),.cpu_sram_wdata (cpu_data_wdata),.cpu_sram_rdata (cpu_data_rdata),.ram_data (ext_ram_data ),.ram_addr (ext_ram_addr ),.ram_be_n (ext_ram_be_n ),.ram_ce_n (ext_ram_ce_n ),.ram_oe_n (ext_ram_oe_n ),.ram_we_n (ext_ram_we_n )
);//cpu
mycpu_top u_mycpu_top(.clk (my_clk ),.resetn (~my_reset ), //low active// inst.inst_sram_en (cpu_inst_en ),.inst_sram_we (cpu_inst_we ),.inst_sram_addr (cpu_inst_addr ),.inst_sram_wdata (cpu_inst_wdata),.inst_sram_rdata (cpu_inst_rdata),// data.data_sram_en (cpu_data_en ),.data_sram_we (cpu_data_we ),.data_sram_addr (cpu_data_addr ),.data_sram_wdata (cpu_data_wdata),.data_sram_rdata (cpu_data_rdata)// //debug// .debug_wb_pc (debug_wb_pc ),// .debug_wb_rf_we (debug_wb_rf_we ),// .debug_wb_rf_wnum (debug_wb_rf_wnum ),// .debug_wb_rf_wdata(debug_wb_rf_wdata)
);
// 这部分代码注释掉
// // 不使用内存、串口时,禁用其使能信号
// assign base_ram_ce_n = 1'b1;
// assign base_ram_oe_n = 1'b1;
// assign base_ram_we_n = 1'b1;// assign ext_ram_ce_n = 1'b1;
// assign ext_ram_oe_n = 1'b1;
// assign ext_ram_we_n = 1'b1;
conver_ram.v
用于实现cpu_ram和base_ram、ext_ram之间信号的转换适配。
module conver_ram (input clk ,input resetn ,// CPU RAM signalinput cpu_sram_en ,input [ 3:0] cpu_sram_we ,input [31:0] cpu_sram_addr ,input [31:0] cpu_sram_wdata ,output reg [31:0] cpu_sram_rdata ,// BaseRAM/ExtRAM signalinout wire[31:0] ram_data, //RAM数据,低8位与CPLD串口控制器共享output wire[19:0] ram_addr, //RAM地址output wire[ 3:0] ram_be_n, //RAM字节使能,低有效。如果不使用字节使能,请保持为0output wire ram_ce_n, //RAM片选,低有效output wire ram_oe_n, //RAM读使能,低有效output wire ram_we_n //RAM写使能,低有效
);
// most import is ram_be_n signal
// if write, cpu_we=1(high si valid), ram_be_n=0(low is valid)
// if read , ram_be_n=0000(low is valid)
// assign sram_be_n = ~(|wen&&en ? wen : 4'hf);
assign ram_be_n = ( | cpu_sram_we && cpu_sram_en) ? ~cpu_sram_we : 4'h0;
assign ram_ce_n = ~cpu_sram_en;
assign ram_we_n = ~( | cpu_sram_we && cpu_sram_en);
assign ram_oe_n = ( | cpu_sram_we && cpu_sram_en);
// 按字节寻址
assign ram_addr = cpu_sram_addr[21:2];assign ram_data = ~ram_we_n ? cpu_sram_wdata : 32'hz;always @(posedge clk or negedge resetn) beginif (resetn) begincpu_sram_rdata <= 32'hz;end else if (~ram_oe_n) begincpu_sram_rdata <= ram_data;end
endendmodule
IF_stage
always @(posedge clk) beginif (reset) beginfs_pc <= 32'h7FFFFFFC; //7FFFFFFC, trick: to make nextpc be 0x80000000 during reset // 80000000 7FFFFFFC// 803FFFFFendelse if (to_fs_valid && (fs_allowin || br_taken)) begin// if taken is valid, to skip the delay slot instruction, next_pc should be the instruction after the jump instfs_pc <= nextpc;end
end
测试
自动评测
在线实验
Ext_Ram内容正确(小端序)。
踩坑记录
读写使能控制
- 使用inout端口时要注意读写使能的控制,当读(写)RAM时读使能有效、写使能无效(读使能无效、写使能有效)
- 读使能和写使能同一时间只能有一个是有效的。
inout类端口的使用方法
当control信号为真时,三态门导通,这时,DataOut的输出通过双向端口传输到DataBus上。但是DataIn与DataOut直接相连,如何保证DataOut的数据不会影响到DataIn相连的电路呢?
解决这个问题的办法是把DataIn声明为reg型,而reg型的变量在always过程块中被赋值,需要再增加一个控制信号,由always敏感表列监控(或者在always内部使用if语句控制是否赋值),以此保证inout端口作为输出时不会影响DataIn。
inout类型端口在使用时,需要将读取和写入分开,读取inout的数据需要设置一个reg作为缓冲。
- 当写使能信号有效时,将写数据赋值给inout端口。否则(读使能有效)将inout赋值为高阻态Z。
- 当读使能有效时,inout端口为高阻态Z并接收来自RAM的数据,然后inout端口将RAM的值赋给cpu_sram_rdata(reg)。
assign ram_data = ~ram_we_n ? cpu_sram_wdata : 32'hz;
always @(posedge clk or negedge resetn) beginif (resetn) begincpu_sram_rdata <= 32'hz;end else begincpu_sram_rdata <= ram_data;end
end
Vivado生成Bit流文件时出现[Synth 8-91] ambiguous clock in event control
时序逻辑的always块,某个信号沿作为触发条件,但是却没有在always块中使用该信号的话,在生成bit流文件时就会出现此类错误。
// 错误
always@(posedge clk_10M or negedge locked) beginreset_of_clk10M <= ~locked;
end
// 正确
always@(posedge clk_10M or negedge locked) beginif(~locked) beginreset_of_clk10M <= 1'b1;end else beginreset_of_clk10M <= 1'b0;end
end
RAM字节使能的使用
- 控制信号有3个,以BaseRAM为例,分别是Base_RAM_EN、Base_RAM_OE和Base_RAM_WE。
- RAM芯片上还有字节使能信号RAM_BE_N[0…3],低电平有效,分别用于使能32位数据线的四个字节 [7…0] [15…8] [23…16] [31…24],当字节使能信号对应位为’1’时,相应的字节输出为高阻态’Z’。(本文中设置为4’b0000即可)
- 从时序要求上看,读取SRAM时要提前准备好地址,并将数据线设置成高阻,然后就可以读出数据了;写SRAM时要提前准备好地址和数据,然后将写信号拉低即可将数据写入相应地址。
| 运算
x=10001001
|x <==> 1|0|0|0|1|0|0|1
| cpu_sram_we && cpu_sram_en
当RAM使能有效,且4位的we信号中只要有1位为1,那么表达式就等于1
参考资料
[1] CPU设计实战(汪文祥)第一、二、三、四章
[2] 龙芯架构32位精简版参考手册
[3] 龙芯杯历届资料
[4] Loongarch个人赛指令集
[5] 自主龙架构CPU设计实验平台
[6] Verilog中双向端口(inout)的原理和使用方法
[7] 计算机组成原理(2021年)实验3 SRAM 实验