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

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

===

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>
  • 1
<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>
  • 1
  • 2

(

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

// 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>
  • 1

// 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>
  • 1

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>
  • 1

// 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>
  • 1

);

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

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>
  • 1

initial begin

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

ram[i]

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

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>
  • 1

ram[ram_addr]

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

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>
  • 1
       

    ``` #### **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元 红包规则

      易百纳技术社区