数字IC设计实战:逐步升级,Tiny—RISC CPU 设计·取指模块设计

 Ref:http://t.csdnimg.cn/BEuY9 作者:小汪的IC自习室

本文主要记录一下上边文章中的一些重要片段,以及对一些可能存在的模糊片段进行解释

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

关于该tiny-risc的程序转化过程:

        程序经编译器编译后生成二进制指令码,烧写程序即烧写程序对应的二进制指令码,烧写程序到硬件系统中后,这些二进制指令码就保存在系统的硬盘中。

        当你打开一个程序的时候,就把对应的指令码+数据加载到了内存里,其中指令部分被随后加载到了CPU的缓存里面,然后CPU根据程序计数器,即程序指针PC指向的缓存地址,把存储的指令从缓存取到指令寄存器里面保存,这里是取指

        你取过来的指令,是由操作码地址码组成的,分别表示 执行什么操作 和 对谁操作 ,但是这些指令需要控制单元里面的一个叫做译码器的东西来分析,分析这些取来的指令码到底是什么意思,然后根据分析出来的内容指定下一步的行动计划:去哪里找什么部件执行什么操作,这是译码。

        随后数据寄存器把数据从内存里面加载到算术逻辑单元,进行运算,并把结果传回数据寄存器,这就是执行。(其实这里包含了写回)

关于rom的地址读取的解释

    always @ (posedge clk) begin
 
        if (we_i == `WriteEnable) begin
 
            _rom[addr_i[31:2]] <= data_i;
 
        end
 
    end

为什么写指令时rom的地址读取采用的是addr_i[31:2]?

因为你要保证rom的读取是1个1个的连续去读

pc所指的是byte为单位,所以pc每次+4,也就是4*8 = 32

rom所指的是word为单位 ,pc变4,它变1,最直接想到的就是100和000了

000 + 4 之后变 100 ,100 + 4之后变 1000 也就是说舍去后两位之后,你的地址就是连续变化的

还有一个疑问:

既然PC是以Byte为单位的,也就是说这32bit实际上都是以Byte为单位的,取出前30位

那么这30位就是以word为单位的,我们也知道,代码在定义rom时规定了线宽为32,深度为4096

rom本身单位是word,但它只有4096位,不会超出吗?还是说只是采取了部分区域的地址作为取指输入?

以下是询问ChatGPT3.5的结果:

问:在verilog中,如果我定义 reg [31:0] ROM [0:4095],那么以后在我调用ROM的时候,ROM[X]中X表示的是那4096个数 还是 指的是 32*4096 这个范围内的地址?

答:在Verilog中,如果您定义了 reg [31:0] ROM [0:4095],那么在调用 ROM 时,ROM[X] 中的 X 表示的是 4096 个 32 位数中的一个数,而不是一个地址。这意味着您可以通过 ROM[0] 到 ROM[4095] 来访问这些 32 位数。

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

难以理解,所以转战原文章

架构图:

PC寄存器: pc

         用于产生PC寄存器的值,该值会被用作指令存储器的地址信号。

序号信号名I/O位宽说明
1clk输入1时钟
2rst输入1复位
3jump_flag_i输入1跳转标志
4jump_addr_i输入32跳转目标地址
5hold_flag_i输入3暂停标志
6jtag_reset_flag_i输入1复位标志
7pc_o输出32

要被取指的PC值

PC寄存器设计端口如上

        PC功能收集:

                        复位PC;跳转PC;连读PC;暂停PC

        关键字:指令对齐:以及为什么PC寄存器单位为Byte而非bit

                                        我认为的PC值本身表示bit,表示Byte的原因应该在于ROM的单位应该是Byte,但是ROM的单位是word

通用寄存器功能收集: csr 和 一般的 reg

                        端口:时钟和复位

        有写端口的全都有使能信号

        有写/读就有写/读的地址和数据(写数据为输入,读数据为输出)

                执行模块只有写

                jtag模块有写,还有读数据

                译码模块只有读,并且分两个寄存器读/写

                                因为涉及操作数和立即数的运算,所以需要两个寄存器来存放操作数

        设计读写操作时的注意事项:

                读地址和写地址重合,但是写操作是时序的,读操作是立即(组合)的,如果两个指令相互关联,那么就直接将写的给读的就好

                写操作要求优先执行模块,后jtag,而且注意写地址不可以是0地址

        关于csr寄存器采用类似的规则去看就可以了,看过了没什么超出的内容

代码如下:

        PC模块

//pc_reg:PC寄存器模块,用于产生PC值,该值会被用作指令存储器的地址信号。
//pc 连接:jtag ctrl bus if_id
//主要功能: 1.系统停止 2.系统复位 3.连续取指 4.跳转指令
`include "defines.v"
module pc_reg(
    input wire clk,
    input wire rst,

    input wire jmp_flg_i,
    input wire [`InstAddrBus] jmp_addr_i,
    input wire [`Hold_Flag_Bus] hld_flg_i,
    input wire jtag_rst_flg_i,

    output reg [`InstAddrBus] pc_o

);

    always @(posedge clk) begin
        if(rst == `RstEnable || jtag_rst_flg_i == 1'b1)begin
            pc_o <= `CpuResetAddr;
        end else if (jmp_flg_i == `JumpEnable) begin
            pc_o <= jmp_addr_i;
        end else if (hld_flg_i == `HoldEnable) begin
            pc_o <= pc_o;
        end else begin
            pc_o <= pc_o + 4'h4;
        end 
    end


endmodule

  那32个寄存器的模块:

//
//一共有32个通用寄存器x0~x31,其中寄存器x0是只读寄存器并且其值固定为0。
// 连接器件: ex  jtag  id  
// 寄存器对ex(执行模块)来说:
//      主要作用在于 存储临时数据
//                  数据传递
//                  存储变量
//                  存储地址
//                  这里的主要作用在于写回
// 寄存器对 id (译码模块)来说:
//      主要作用在于 存储指令操作数(方便执行)
//                  存储指令结果(方便执行)
//                  存储指令地址(方便内存访问)
//                  传递控制信号(控制信号 给执行)
// 寄存器对 jtag(调试接口)来说:
//                  在调试和测试过程中,
//                  JTAG接口可以用来读取和写入通用寄存器中的数据,
//                  以帮助分析程序执行过程中的数据和状态

`include "defines.v"

module regs(

    input wire clk,
    input wire rst,

    //from 执行,这里是写回操作
    input wire wr_ena_i,
    input wire [`RegAddrBus] wr_addr_i,
    input wire [`RegBus] wr_dat_i,
    
    //from 调试口
    input wire jtag_wr_en_i,
    input wire [`RegAddrBus] jtag_addr_i,
    input wire [`RegBus] jtag_dat_i
    //to 调试口
    output reg [`RegBus] jtag_dat_o

    //from 译码模块
    input wire [`RegAddrBus] rd_addr1_i,
    input wire [`RegAddrBus] rd_addr2_i,
    //to 译码模块
    output wire [`RegBus] rd_dat1_o,
    output wire [`RegBus] rd_dat2_o,
);

reg[`RegBus] regs [0:`RegNum - 1];

    //写寄存器
    always @(posedge clk) begin
        if(rst == `RstDisable) begin
            if((wr_ena_i == `WriteEnable) && (wr_addr_i != `ZeroReg))begin
                regs[wr_addr_i] <= wr_dat_i;
            end else if ((jtag_wr_en_i == `WriteEnable) && (jtag_addr_i != `ZeroReg)) begin
                regs[jtag_addr_i] <= jtag_dat_i;
            end
        end
    end

    //读寄存器1
    always @ (*) begin
        if (rd_addr1_i == `ZeroReg) begin
            rd_dat1_o = `ZeroWord;
        // 如果读地址等于写地址,并且正在写操作,则直接返回写数据
        end else if (rd_addr1_i == wr_addr_i && wr_ena_i == `WriteEnable) begin
            rd_dat1_o = wr_dat_i;
        end else begin
            rd_dat1_o = regs[rd_addr1_i];
        end
    end

    //读寄存器2
    always @ (*) begin
        if(rd_addr2_i == `ZeroReg) begin
            rd_dat2_o = `ZeroWord;
        end else if (rd_addr2_i == wr_addr_i && wr_ena_i == `WriteEnable) begin
            rd_dat2_o = wr_dat_i;
        end else begin
            rd_dat2_o = regs[rd_addr2_i];
        end
    end

    // jtag读寄存器
    always @(*) begin
        if(jtag_addr_i == `ZeroReg) begin
            jtag_dat_o = `ZeroWord;
        end else begin
            jtag_dat_o = regs[jtag_addr_i];
        end
    end

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值