上回书说到,指令集建立好了,这次我们来做CPU的设计!
作者:Trustintruth
来源:https://zhuanlan.zhihu.com/p/97064690
在说数字通路之前,我们先对几个基础模块进行设计。(说实话,控制信号太多了导致篇幅太长了,经咨询决定数据通路及控制都交给下篇)
宏定义
首先交代宏定义
存储器(存放数据和程序)
可以理解为存储着程序的ROM,当CPU启动时从0地址开始执行程序。在设计这个存储器使用Single Port ROM
使用Xilinx FPGA Block RAM 单端口的RAM
下面一个always块设计来产生就绪信号,当复位信号到来时进行初始化,在片选信号和地址选通信号同时有效时,就绪信号变为有效
x_s3e_sprom x_s3e_sprom (
.clka (clk),
.addra (addr),
.douta (rd_data)
);
/********** ready signal**********/
always @(posedge clk or `RESET_EDGE reset) begin
if (reset == `RESET_ENABLE) begin
rdy_ <= #1 `DISABLE_;
end else begin
if ((cs_ == `ENABLE_) && (as_ == `ENABLE_)) begin
rdy_ <= #1 `ENABLE_;
end else begin
rdy_ <= #1 `DISABLE_;
end
end
end
endmodule
总线(BUS)
总线是将CPU、内存和I/O相互连接的共享通道。我们的CPU设计使用时钟信号同步数据传输的同步总线。具体实现交由下
CPU小部件
1.通用寄存器:指令集限制最大可以指定三个寄存器作为操作数,从两个寄存器中取操作数进行操作,随后将结果写入第三个寄存器中。所以才设计通用寄存器时需要两个读取端口和一个写入端口,同时自然地需要在写入端口加入写使能信号以保障写入数据正确。
读取操作即为两个assign语句实现,注意在读取的同时对相同地址进行写入操作时,则直接将写入的数据输出。
写入操作时,首先异步复位,寄存器初始化为0,当有效信号有效时,向指定地址单元写入制定数据。
/********** Read(Write After Read) **********/
// readport 0
assign rd_data_0 = ((we_ == `ENABLE_) && (wr_addr == rd_addr_0)) ?
wr_data : gpr[rd_addr_0];
// readport 1
assign rd_data_1 = ((we_ == `ENABLE_) && (wr_addr == rd_addr_1)) ?
wr_data : gpr[rd_addr_1];
/********** write **********/
always @ (posedge clk or `RESET_EDGE reset) begin
if (reset == `RESET_ENABLE) begin
for (i = 0; i < `REG_NUM; i = i + 1) begin
gpr[i] <= #1 `WORD_DATA_W'h0;
end
end else begin
if (we_ == `ENABLE_) begin
gpr[wr_addr] <= #1 wr_data;
end
end
end
2.SPM
在本CPU设计中增加了一个可以不经过总线直接访问的专用内存,其中包括一个Dual Port RAM,他的宏为:
首先IF阶段(后面我们介绍这部分,现在先理解为一个阶段)的地址有效信号又消失,读写判断信号判断为写入时,A端口(portA)写入有效信号为有效。同理下面B端口,只不过是判断MEM阶段的地址有效信号和读写判断信号为写入。
always @(*) begin
/*Port A */
if ((if_spm_as_ == `ENABLE_) && (if_spm_rw == `WRITE)) begin
wea = `MEM_ENABLE; // able
end else begin
wea = `MEM_DISABLE; // disable
end
/* Port B */
if ((mem_spm_as_ == `ENABLE_) && (mem_spm_rw == `WRITE)) begin
web = `MEM_ENABLE; // able
end else begin
web = `MEM_DISABLE; // disable
end
end
/********** Xilinx FPGA Block RAM **********/
x_s3e_dpram x_s3e_dpram (
/**********Port A IF**********/
.clka (clk),
.addra (if_spm_addr),
.dina (if_spm_wr_data),
.wea (wea),
.douta (if_spm_rd_data),
/********** Port B : MEM **********/
.clkb (clk),
.addrb (mem_spm_addr),
.dinb (mem_spm_wr_data),
.web (web),
.doutb (mem_spm_rd_data)
);
3.ALU
ALU,算数逻辑单元是根据输入指定的操作对数据进行处理,并输出处理结果。在本次的设计中,ALU的输入为一个操作码和两个数据,输出为运算结果和溢出信号。ALU承担着CPU中的逻辑预算数运算的任务,所以基础的操作需要包含加法,减法(补码加法),左移,右移,与,或等操作,设计框图如下:
always @(*) begin
case (op)
`ALU_OP_AND : begin //iAND
out = in_0 & in_1;
end
`ALU_OP_OR : begin // iOR
out = in_0 | in_1;
end
`ALU_OP_XOR : begin // iXOR
out = in_0 ^ in_1;
end
`ALU_OP_ADDS : begin
out = in_0 + in_1;
end
`ALU_OP_ADDU : begin
out = in_0 + in_1;
end
`ALU_OP_SUBS : begin
out = in_0 - in_1;
end
`ALU_OP_SUBU : begin
out = in_0 - in_1;
end
`ALU_OP_SHRL : begin
out = in_0 >> in_1[`ShAmountLoc];
end
`ALU_OP_SHLL : begin
out = in_0 << in_1[`ShAmountLoc];
end
default : begin // (No Operation)
out = in_0;
end
endcase
end
/********** �I�[�o�t���[�`�F�b�N **********/
always @(*) begin
case (op)
`ALU_OP_ADDS : begin
if (((s_in_0 > 0) && (s_in_1 > 0) && (s_out < 0)) ||
((s_in_0 < 0) && (s_in_1 < 0) && (s_out > 0))) begin
of = `ENABLE;
end else begin
of = `DISABLE;
end
end
`ALU_OP_SUBS : begin
if (((s_in_0 < 0) && (s_in_1 > 0) && (s_out > 0)) ||
((s_in_0 > 0) && (s_in_1 < 0) && (s_out < 0))) begin
of = `ENABLE;
end else begin
of = `DISABLE;
end
end
default : begin
of = `DISABLE;
end
endcase
end
推荐阅读
关注此系列,请关注专栏FPGA的逻辑