【MCU】16bit CPU设计实战(一)

david 2022-02-22 09:00:14 3554

===

ARM CPU的完备SDK、软件生态对于加速MCU设计而言可谓是有如神助,作为ICer,我在设计完成MCU RTL后,即可利用ARM提供的SDK快速完成MCU的系统验证,避免要自己一一开发软件驱动的重复性繁琐工作。

ARM是否被英伟达收购犹未可知,海思的麒麟芯片的CPU、mali GPU仍是公版arm软核,受制于人。

我曾经预言过海思手机芯片的三大卡脖子:

1、arm授权的CPU、Mali GPU

2、安卓系统(鸿蒙 逆境而生)

3、台积电代工(当时预测还被喷)

先进工艺代工问题非常痛苦,那么ARM的CPU、GPU问题依然棘手,相信海思未来能开发自己的自主可控指令集、CPU、GPU,像鸿蒙一样独立自主。
为了自己可控,RISC-V的崛起之路仍需要加强生态建设,本文的主角,还是选取学校科研教学常用的MIPS指令集。

以中科龙芯采用的MIPS架构为例,本CPU设计架构图如下:

The Instruction Format and Instruction Set Architecture for the 16-bit single-cycle MIPS are as follows:

Instruction set for the MIPS processor

Instruction Set Architecture for the MIPS processor

指令描述

我们选取更为容易实现的单周期指令来实现CPU设计:1. Add : R[rd] = R[rs] + R[rt]

  1. Subtract : R[rd] = R[rs] - R[rt]
  2. And: R[rd] = R[rs] & R[rt]
  3. Or : R[rd] = R[rs] | R[rt]
  4. SLT: R[rd] = 1 if R[rs]
  5. Jr: PC=R[rs]
  6. Lw: R[rt] = M[R[rs]+SignExtImm]
  7. Sw : M[R[rs]+SignExtImm] = R[rt]
  8. Beq : if(R[rs]==R[rt]) PC=PC+1+BranchAddr
  9. Addi: R[rt] = R[rs] + SignExtImm
  10. J : PC=JumpAddr
  11. Jal : R[7]=PC+2;PC=JumpAddr
  12. SLTI: R[rt] = 1 if R[rs]

SignExtImm = { 9{immediate[6]}, imm}

JumpAddr = { (PC+1)[15:13], address}

BranchAddr = { 7{immediate[6]}, immediate, 1’b0 }

CPU数据通路、控制通路

// Submodule: Data memory in Verilog

<pre style="line-height: 23.1px;"><span style="color: rgb(0, 136, 0);font-weight: bold;"></span>
<pre class="code-snippet__js" data-lang="properties">```
<span class="code-snippet_outer"><span class="code-snippet__attr">module</span> <span class="code-snippet__string">data_memory  </span></span>

(

<span class="code-snippet_outer">      <span class="code-snippet__attr">input</span>                         <span class="code-snippet__string">clk,  </span></span>

// address input, shared by read and write port

<span class="code-snippet_outer">      <span class="code-snippet__attr">input</span>     <span class="code-snippet__string">[15:0]               mem_access_addr,  </span></span>

// write port

<span class="code-snippet_outer">      <span class="code-snippet__attr">input</span>     <span class="code-snippet__string">[15:0]               mem_write_data,  </span></span>

input mem_write_en,

<span class="code-snippet_outer">      <span class="code-snippet__attr">input</span> <span class="code-snippet__string">mem_read,  </span></span>

// read port

<span class="code-snippet_outer">      <span class="code-snippet__attr">output</span>     <span class="code-snippet__string">[15:0]               mem_read_data  </span></span>

);

<span class="code-snippet_outer">      <span class="code-snippet__attr">integer</span> <span class="code-snippet__string">i;  </span></span>

reg [15:0] ram [255:0];

<span class="code-snippet_outer">      <span class="code-snippet__attr">wire</span> <span class="code-snippet__string">[7 : 0] ram_addr = mem_access_addr[8 : 1];  </span></span>

initial begin

<span class="code-snippet_outer">           <span class="code-snippet__meta">for(i</span>=<span class="code-snippet__string">0;i</span></span>

ram[i]

<span class="code-snippet_outer">      <span class="code-snippet__attr">end</span>  <span class="code-snippet__string"></span></span>

always @(posedge clk) begin

<span class="code-snippet_outer">           <span class="code-snippet__attr">if</span> <span class="code-snippet__string">(mem_write_en)  </span></span>

ram[ram_addr]

<span class="code-snippet_outer">      <span class="code-snippet__attr">end</span>  <span class="code-snippet__string"></span></span>

assign mem_read_data = (mem_read==1'b1) ? ram[ram_addr]: 16'd0;

<span class="code-snippet_outer"> <span class="code-snippet__attr">endmodule</span></span>
   

``` #### **Verilog code for ALU Control unit:** ```
// Submodule: ALU Control Unit in Verilog

``` - - - - - - - - - - - - - - - - - - - - - - - - - - ```
```
 module ALUControl( ALU_Control, ALUOp, Function);  
``````
 output reg[2:0] ALU_Control;  
``````
 input [1:0] ALUOp;  
``````
 input [3:0] Function;  
``````
 wire [5:0] ALUControlIn;  
``````
 assign ALUControlIn = {ALUOp,Function};  
``````
 always @(ALUControlIn)  
``````
 casex (ALUControlIn)  
``````
  6'b11xxxx: ALU_Control=3'b000;  
``````
  6'b10xxxx: ALU_Control=3'b100;  
``````
  6'b01xxxx: ALU_Control=3'b001;  
``````
  6'b000000: ALU_Control=3'b000;  
``````
  6'b000001: ALU_Control=3'b001;  
``````
  6'b000010: ALU_Control=3'b010;  
``````
  6'b000011: ALU_Control=3'b011;  
``````
  6'b000100: ALU_Control=3'b100;  
``````
  default: ALU_Control=3'b000;  
``````
  endcase  
``````
 endmodule  
``````
// Verilog code for JR control unit
``````
module JR_Control( input[1:0] alu_op, 
``````
       input [3:0] funct,
``````
       output JRControl
``````
    );
``````
assign JRControl = ({alu_op,funct}==6'b001000) ? 1'b1 : 1'b0;
``````
endmodule
```
```

```


``` #### **Verilog code for control unit:** ```


// Submodule: Control Unit in Verilog

``` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ```
```
 module control( input[2:0] opcode,  
``````
                           input reset,  
``````
                           output reg[1:0] reg_dst,mem_to_reg,alu_op,  
``````
                           output reg jump,branch,mem_read,mem_write,alu_src,reg_write,sign_or_zero                      
``````
   );  
``````
 always @(*)  
``````
 begin  
``````
      if(reset == 1'b1) begin  
``````
                reg_dst = 2'b00;  
``````
                mem_to_reg = 2'b00;  
``````
                alu_op = 2'b00;  
``````
                jump = 1'b0;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b0;  
``````
                reg_write = 1'b0;  
``````
                sign_or_zero = 1'b1;  
``````
      end  
``````
      else begin  
``````
      case(opcode)   
``````
      3'b000: begin // add  
``````
                reg_dst = 2'b01;  
``````
                mem_to_reg = 2'b00;  
``````
                alu_op = 2'b00;  
``````
                jump = 1'b0;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b0;  
``````
                reg_write = 1'b1;  
``````
                sign_or_zero = 1'b1;  
``````
                end  
``````
      3'b001: begin // sli  
``````
                reg_dst = 2'b00;  
``````
                mem_to_reg = 2'b00;  
``````
                alu_op = 2'b10;  
``````
                jump = 1'b0;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b1;  
``````
                reg_write = 1'b1;  
``````
                sign_or_zero = 1'b0;  
``````
                end  
``````
      3'b010: begin // j  
``````
                reg_dst = 2'b00;  
``````
                mem_to_reg = 2'b00;  
``````
                alu_op = 2'b00;  
``````
                jump = 1'b1;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b0;  
``````
                reg_write = 1'b0;  
``````
                sign_or_zero = 1'b1;  
``````
                end  
``````
      3'b011: begin // jal  
``````
                reg_dst = 2'b10;  
``````
                mem_to_reg = 2'b10;  
``````
                alu_op = 2'b00;  
``````
                jump = 1'b1;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b0;  
``````
                reg_write = 1'b1;  
``````
                sign_or_zero = 1'b1;  
``````
                end  
``````
      3'b100: begin // lw  
``````
                reg_dst = 2'b00;  
``````
                mem_to_reg = 2'b01;  
``````
                alu_op = 2'b11;  
``````
                jump = 1'b0;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b1;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b1;  
``````
                reg_write = 1'b1;  
``````
                sign_or_zero = 1'b1;  
``````
                end  
``````
      3'b101: begin // sw  
``````
                reg_dst = 2'b00;  
``````
                mem_to_reg = 2'b00;  
``````
                alu_op = 2'b11;  
``````
                jump = 1'b0;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b1;  
``````
                alu_src = 1'b1;  
``````
                reg_write = 1'b0;  
``````
                sign_or_zero = 1'b1;  
``````
                end  
``````
      3'b110: begin // beq  
``````
                reg_dst = 2'b00;  
``````
                mem_to_reg = 2'b00;  
``````
                alu_op = 2'b01;  
``````
                jump = 1'b0;  
``````
                branch = 1'b1;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b0;  
``````
                reg_write = 1'b0;  
``````
                sign_or_zero = 1'b1;  
``````
                end  
``````
      3'b111: begin // addi  
``````
                reg_dst = 2'b00;  
``````
                mem_to_reg = 2'b00;  
``````
                alu_op = 2'b11;  
``````
                jump = 1'b0;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b1;  
``````
                reg_write = 1'b1;  
``````
                sign_or_zero = 1'b1;  
``````
                end  
``````
      default: begin  
``````
                reg_dst = 2'b01;  
``````
                mem_to_reg = 2'b00;  
``````
                alu_op = 2'b00;  
``````
                jump = 1'b0;  
``````
                branch = 1'b0;  
``````
                mem_read = 1'b0;  
``````
                mem_write = 1'b0;  
``````
                alu_src = 1'b0;  
``````
                reg_write = 1'b1;  
``````
                sign_or_zero = 1'b1;  
``````
                end  
``````
      endcase  
``````
      end  
``````
 end  
``````
 endmodule  
```
```

### **Verilog code for the single-cycle MIPS processor:**

- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 

```
```
 module mips_16( input clk,reset,  
``````
                           output[15:0] pc_out, alu_result
``````
                           //,reg3,reg4  
``````
   );  
``````
 reg[15:0] pc_current;  
``````
 wire signed[15:0] pc_next,pc2;  
``````
 wire [15:0] instr;  
``````
 wire[1:0] reg_dst,mem_to_reg,alu_op;  
``````
 wire jump,branch,mem_read,mem_write,alu_src,reg_write     ;  
``````
 wire     [2:0]     reg_write_dest;  
``````
 wire     [15:0] reg_write_data;  
``````
 wire     [2:0]     reg_read_addr_1;  
``````
 wire     [15:0] reg_read_data_1;  
``````
 wire     [2:0]     reg_read_addr_2;  
``````
 wire     [15:0] reg_read_data_2;  
``````
 wire [15:0] sign_ext_im,read_data2,zero_ext_im,imm_ext;  
``````
 wire JRControl;  
``````
 wire [2:0] ALU_Control;  
``````
 wire [15:0] ALU_out;  
``````
 wire zero_flag;  
``````
 wire signed[15:0] im_shift_1, PC_j, PC_beq, PC_4beq,PC_4beqj,PC_jr;  
``````
 wire beq_control;  
``````
 wire [14:0] jump_shift_1;  
``````
 wire [15:0]mem_read_data;  
``````
 wire [15:0] no_sign_ext;  
``````
 wire sign_or_zero;  
``````
 // PC   
``````
 always @(posedge clk or posedge reset)  
``````
 begin   
``````
      if(reset)   
``````
           pc_current 
``````
      else  
``````
           pc_current 
``````
 end  
``````
 // PC + 2   
``````
 assign pc2 = pc_current + 16'd2;  
``````
 // instruction memory  
``````
 instr_mem instrucion_memory(.pc(pc_current),.instruction(instr));  
``````
 // jump shift left 1  
``````
 assign jump_shift_1 = {instr[13:0],1'b0};  
``````
 // control unit  
``````
 control control_unit(.reset(reset),.opcode(instr[15:13]),.reg_dst(reg_dst)  
``````
                ,.mem_to_reg(mem_to_reg),.alu_op(alu_op),.jump(jump),.branch(branch),.mem_read(mem_read),  
``````
                .mem_write(mem_write),.alu_src(alu_src),.reg_write(reg_write),.sign_or_zero(sign_or_zero));  
``````
 // multiplexer regdest  
``````
 assign reg_write_dest = (reg_dst==2'b10) ? 3'b111: ((reg_dst==2'b01) ? instr[6:4] :instr[9:7]);  
``````
 // register file  
``````
 assign reg_read_addr_1 = instr[12:10];  
``````
 assign reg_read_addr_2 = instr[9:7];  
``````
 register_file reg_file(.clk(clk),.rst(reset),.reg_write_en(reg_write),  
``````
 .reg_write_dest(reg_write_dest),  
``````
 .reg_write_data(reg_write_data),  
``````
 .reg_read_addr_1(reg_read_addr_1),  
``````
 .reg_read_data_1(reg_read_data_1),  
``````
 .reg_read_addr_2(reg_read_addr_2),  
``````
 .reg_read_data_2(reg_read_data_2)); 
``````
 //.reg3(reg3),  
``````
 //.reg4(reg4));  
``````
 // sign extend  
``````
 assign sign_ext_im = {{9{instr[6]}},instr[6:0]};  
``````
 assign zero_ext_im = {{9{1'b0}},instr[6:0]};  
``````
 assign imm_ext = (sign_or_zero==1'b1) ? sign_ext_im : zero_ext_im;  
``````
 // JR control  
``````
 JR_Control JRControl_unit(.alu_op(alu_op),.funct(instr[3:0]),.JRControl(JRControl));       
``````
 // ALU control unit  
``````
 ALUControl ALU_Control_unit(.ALUOp(alu_op),.Function(instr[3:0]),.ALU_Control(ALU_Control));  
``````
 // multiplexer alu_src  
``````
 assign read_data2 = (alu_src==1'b1) ? imm_ext : reg_read_data_2;  
``````
 // ALU   
``````
 alu alu_unit(.a(reg_read_data_1),.b(read_data2),.alu_control(ALU_Control),.result(ALU_out),.zero(zero_flag));  
``````
 // immediate shift 1  
``````
 assign im_shift_1 = {imm_ext[14:0],1'b0};  
``````
 //  
``````
 assign no_sign_ext = ~(im_shift_1) + 1'b1;  
``````
 // PC beq add  
``````
 assign PC_beq = (im_shift_1[15] == 1'b1) ? (pc2 - no_sign_ext): (pc2 +im_shift_1);  
``````
 // beq control  
``````
 assign beq_control = branch & zero_flag;  
``````
 // PC_beq  
``````
 assign PC_4beq = (beq_control==1'b1) ? PC_beq : pc2;  
``````
 // PC_j  
``````
 assign PC_j = {pc2[15],jump_shift_1};  
``````
 // PC_4beqj  
``````
 assign PC_4beqj = (jump == 1'b1) ? PC_j : PC_4beq;  
``````
 // PC_jr  
``````
 assign PC_jr = reg_read_data_1;  
``````
 // PC_next  
``````
 assign pc_next = (JRControl==1'b1) ? PC_jr : PC_4beqj;  
``````
 // data memory  
``````
 data_memory datamem(.clk(clk),.mem_access_addr(ALU_out),  
``````
 .mem_write_data(reg_read_data_2),.mem_write_en(mem_write),.mem_read(mem_read),  
``````
 .mem_read_data(mem_read_data));  
``````
 // write back  
``````
 assign reg_write_data = (mem_to_reg == 2'b10) ? pc2:((mem_to_reg == 2'b01)? mem_read_data: ALU_out);  
``````
 // output  
``````
 assign pc_out = pc_current;  
``````
 assign alu_result = ALU_out;  
``````
 endmodule 
```
```

### **Verilog testbench code for the single-cycle MIPS processor:**

- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 

```
```
 `timescale 1ns / 1ps
``````
// Verilog project: Verilog code for 16-bit MIPS Processor
``````
// Testbench Verilog code for 16 bit single cycle MIPS CPU  
``````
 module tb_mips16;  
``````
      // Inputs  
``````
      reg clk;  
``````
      reg reset;  
``````
      // Outputs  
``````
      wire [15:0] pc_out;  
``````
      wire [15:0] alu_result;//,reg3,reg4;  
``````
      // Instantiate the Unit Under Test (UUT)  
``````
      mips_16 uut (  
``````
           .clk(clk),   
``````
           .reset(reset),   
``````
           .pc_out(pc_out),   
``````
           .alu_result(alu_result)  
``````
           //.reg3(reg3),  
``````
          // .reg4(reg4)  
``````
      );  
``````
      initial begin  
``````
           clk = 0;  
``````
           forever #10 clk = ~clk;  
``````
      end  
``````
      initial begin  
``````
           // Initialize Inputs  
``````
           //$monitor ("register 3=%d, register 4=%d", reg3,reg4);  
``````
           reset = 1;  
``````
           // Wait 100 ns for global reset to finish  
``````
           #100;  
``````
     reset = 0;  
``````
           // Add stimulus here  
``````
      end  
``````
 endmodule
```
```

感谢阅读,别走!点赞、关注、转发后再走吧

![](https://ebaina.oss-cn-hangzhou.aliyuncs.com/wechat-official-crawl/2022-02/164549161332962.jpg)

参考链接:

https://www.fpga4student.com/2017/01/verilog-code-for-single-cycle-MIPS-processor.html

转载:全栈芯片工程师
                
声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
david
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
david
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区