实习期间主要是做一个risc-cpu的设计和验证。
关于risc-cpu,大家可以在网上找到挺多的资料。大概包含以下这些模块:
1,算术逻辑运算部件(ALU)
2,累加器
3,程序计数器
4,指令寄存器,译码器
5,时序和控制部件
大致上运行的过程呢,就是 程序计数器 做地址上的操作,跳转一个指定的地址,或者自加。每个地址呢,就是取指令了,指令进行译码,决定下一步的操作,取数据也好,计算,存数据也好。更具体的大家可以寻找资料自己尝试实现一下。
这里我想说说我收获最大的部分,就是在实现的过程,对状态机的使用了。以前在学校学到三段式状态机,还是懵懵懂懂的。在做risc-cpu的时候认认真真的学习后,觉得状态机对于设计而言真的是很便利的。
比如现在有3个状态:IDLE, FETCH, DECODE
大致结构如下,第一段,状态跳转:
always@( posedge clk or negedge rst_n ) begin //状态跳转 时序逻辑
if( !rst_n ) begin
state <= IDLE;
end else begin
state <= next_state;
end
end
第二段,下一状态判断:
always( * ) begin //获得下一状态 组合逻辑
case( state )
IDLE: begin
if( ..... ) next_state = FETCH; //符合某一条件进入下一状态
else next_state = IDLE; //未符合保持在当前状态
end
FETCH:begin
if( ..... ) next_state = DECODE; //符合某一条件进入下一状态
else next_state = FETCH; //未符合保持在当前状态
end
DECODE:begin
if( ..... ) next_state = IDLE; //符合某一条件进入下一状态
else next_state = DECODE; //未符合保持在当前状态
end
default:begin
end
endcase
end
第三段,根据控制与输出:
假设a, b 信号用于不同状态下的输入与控制
always( posedge clk or negedge rst_n ) begin //获得下一状态 组合逻辑
if( !rst_n ) begin
a <= ....
b <= ....
end else begin
case( state )
IDLE: begin
a <= .... //某些赋值, 输出, 控制
b <= ....
end
FETCH:begin
a <= .... //某些赋值, 输出, 控制
b <= ....
end
DECODE:begin
a <= .... //某些赋值, 输出, 控制
b <= ....
end
default:begin
end
endcase
end
三段式的好处个人觉得有二:
一个就是功能上各段分的都很清晰,各段做什么分割的清楚。方便设计,也方便检查。
其二便是对于后续的优化也清晰,当成最简状态机( 状态只有1bit, 输出控制只有1bit )来看,两个dff中间夹着组合一套逻辑,如果综合约束不满足,时钟路径也是便于查找的。
下次讲讲我所理解的验证的思想,方法。