LC3取指模块的定向测试
这篇文章将对LC3微处理器中的一个模块进行定向测试。
Little Computer 3 是一种教学用的汇编语言。
LC3设计包括6个模块:
- fetch 取指
- execute 执行
- writeback 回写
- memAccess 访存
- decode 解码
- controller 控制器
LC3微控制器取值模块如图:![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6913a191f1163a08a5ed53bc6b15f795.png)
fetch模块计算从内存中取数的地址:
- clock,reset:1位;
- br_taken:1位。通知fetch块遇到控制信号,所有npc的值需要从pc+1改变为由指令计算出的地址值taddr(目标地址)。
- taddr:16位。为分支或者跳转指令计算得到的目标地址。
- state:4位。controller块当前状态。
fetch块有如下输出:
- rd:1位。通知内存执行读操作。
- pc:16位。程序计数器寄存器的当前值。
- npc:16位。值始终为PC+1。
在clock的上升沿,当br_taken为真时,taddr送入PC_reg;当br_taken为假时,PC_reg送入npc;PC_reg复位时为16’h3000。所有信号都在同一个周期内更新。
取值模块的Verilog代码
module fetch(clock,reset,state,pc,npc,rd,
taddr,br_taken);
input clock,reset,br_taken;
input[15:0] taddr;
input[3:0] state;
output[15:0] pc,npc;
output rd;
...
endmodule
取值模块的接口
interface fetch_ifc(input bit clock);
logic reset,br_taken,rd;
logic[15:0] taddr;
cntrl_e state;
logic[15:0] pc,npc;
clocking cb @(postedge clock);
input pc,npc,rd;
output taddr,state,br_taken,reset;
endclocking
modport TEST(clocking cb,output reset);
modport DUT(input clock,reset,br_taken,taddr,state,
output pc,npc,rd);
clocking cbm @(postedge clock); //用于监控DUT信号
input pc,npc,rd,taddr,state,br_taken;
endcloking
modport MONITOR(clocking cbm);
endinterface
取值模块的定向测试
program automatic test(fetch_ifc.TEST if_t,
fetch_ifc.MONITOR if_m);
initial begin
cntrl_e cntrl;
$timeformat(-9,0,"ns",5);
$monitor("%t:pc=%h npc=%h rd=%h state=%h",
$realtime,if_m.cbm.pc,if_m.cb.npc,
if_m.cbm.rd,if_m.cbm.state.name);
$display("%t: Reset all signals", $realtime);
if_t.reset <= 1;
if_t.cb.taddr <= 16'hFFFC;
if_t.cb.br_taken <= 0;
if_t.cb.state<= CNTRL_UPDATE_PC;
repeat(2) @(if_t.cb);
pc_post_reset: assert (if_t.cb.pc == 16'h3000);
# # 1 if_t.cb.reset <= 0; //同步地释放复位信号.
// #表示的时间的单位的延时,以你`timescale指定的为最小单位。
//##是systemverilog assertion里面的语法,表示的是时钟周期的延时,他的最小单位为一个clock周期。
@(if_t.cb);
$display("\n%t: Test loading of target address", $realtime);
if_t.cb.state <= CNTRL_UPDATE_PC;
if_t.cb.br_taken <= 1;
@(if_t.cb);
@(if_t.cb);
pc_br_taken : assert (if_t.cb.pc == 16'hFFFC);
$display("%t: Did the PC rollover as expected?", $realtime);
if_t.cb.br_taken <= 0;
if_t.cb.state<= CNTRL_UPDATE_PC;
repeat(5) @(if_t.cb);
pc_rollover: assert (if_t.cb.pc == 16'h0000);
$display("\n%t: Step through all the controller states", $realtime);
for(int i = CNTRL_FETCH; i <= CNTRL_COMPUTE_MEM; i++)
begin
$cast(cntrl,i);
if(cntrl == CNTRL_UPDATE_PC)
continue;
$display("%t: Try with controller state=%0d %s",$realtime,cntrl,cntrl.name);
if_t.cb.br_taken <= 0;
if_t.cb.state<= cntrl;
repeat(2) @(if_t.cb);
pc_no_load: assert (if_t.cb.pc == 16'h0001);
end
$display("\n%t: Tristate on PC output", $realtime);
if_t.cb.state <= CNTRL_READ_MEM;
@(if_t.cb);
pc_z_read_mem: assert(if_t.cb.pc === 16'hzzzz);
if_t.cb.state <= CNTRL_IND_ADDR_RD;
@(if_t.cb);
pc_z_ind_addr_rd: assert(if_t.cb.pc === 16'hzzzz);
if_t.cb.state <= CNTRL_WRITE_MEM;
@(if_t.cb);
pc_z_write_mem: assert(if_t.cb.pc === 16'hzzzz);
end
endprogram
取指测试平台的顶层模块
//顶层模块例化了取指接口、fetch模块和test。
//也定义了控制器状态枚举类型,这样在test和接口中都可以使用。
'timescale 1ns/1ns
typedef enum{CNTRL_UPDATE_PC=0,
CNTRL_FETCH=1,
CNTRL_DECODE=2,
CNTRL_EXECUTE=3,
CNTRL_UPDATE_REGF=4,
CNTRL_COMPUTE_PC=5,
CNTRL_COMPUTE_MEM=6,
CNTRL_READ_MEM=7,
CNTRL_IND_ADDR_RD=8,
CNTRL_WRITE_MEM=9,
} cntrl_e;
module top;
bit clock;
always # 10 clock = ~clock;
fetch_ifc fif(clock);
test t1(fif,fif);
fetch f1(clock, fif.reset, fif.state, fif.pc, fif.npc, fif.rd, fif.taddr, fif.br_taken);
endmodule
结论
通过SV的接口来组织各个设计模块和测试平台间的通信,简化了测试平台的代码量,一个接口可以取代很多的信号连接。SV中也引入了程序块来减少待测模块和测试平台之间的竞争状态,在接口中使用时钟块,测试平台可以相对于时钟正确地驱动和采样设计信号。