极术小能手 · 2022年03月17日

【课程实验附录】 总线译码器

由于本实验的硬件设计部分使用 Verilog HDL 编写, 考虑到很多同学之前并没有接触过 Verilog 或者是使用的 VHDL 语言进行设计, 故本章主要目的是帮助同学们熟悉 Verilog HDL 硬件描述语言以及两个常用的模块: 使能信号和总线译码器, 从而更好的完成本节课的实验.

什么是总线译码器? 有什么用?

假如本实验难度太大, 你想要和自己的朋友哭诉, 这时你需要一部手机和朋友的电话号, 结果很遗憾, 你的朋友并没有接你的电话, 你只能继续打电话给下一个朋友. SoC想要访问多个外设的时候也是如此. 当处理器核只有一个 Master 总线接口时(相当于只有一部手机的时候), 我们此时就需要译码器(运营商)发挥作用. 总线译码器的主要作用是对地址总线上的地址信号进行译码, 之后生成对应外设的选择信号(这个过程相当于拨通了电话). 但是需要注意, 此时并不代表处理器核已经可以访问这个外设了, 生成选择信号后, 需要判断对应外设的使能信号是否拉高(也就是你的朋友有没有挂你的电话), 如果此时使能有效, 则处理器核就可以访问这个外设了.

译码器的常见描述方法

使用组合逻辑直接赋值

以一个常见的 3-8 译码器为例, 我们先列出它的真值表

image.png
根据真值表, 可以直接得出输出的逻辑表达式. 利用 assign 语句, 可以写出对应的 Verilog 代码.

module decoder3t8(
    input a, b,c,
    output yo, yl, y2, y3, y4, y5,y6, y7
) ;
    assign y0 = ~a &~b&~c ;
    assign y1=~a &~b&c;
    assign y2= ~a &b& ~c;
    assign y3= ~a &b &c;
    assign y4=a &~b&~c;
    assign y5=a &~b&c ;
    assign y6 =a &b&~c;
    assign y7 =a &b&c;
    endmodule

这种方法只适用于简单的译码器, 如果译码器位数过多, 会很繁琐.

使用 if-else 语句

如果你觉得使用逻辑表达式输出很麻烦, 你可以直接使用描述性的语言来编写译码器. 使用 if-else 语句描述输入输出之间的关系, 也可以实现译码器. 我们还是以 3-8 译码器为例,

module decoder38if(
    input[2:0] a,
    output reg [7:0] y
);
    always@ (*)begin
        y= 8'b0;

        if(a [2])
        begin
            if(a[1])
            begin
                if(a [0]) y[7] = 1'b1;
                else y[6] = 1'b1;
            end
            else
            begin
                if(a[0]) y[5] = 1'b1;
                else y[4]= 1'b1;
            end
        end
        else
        begin
            if(a[1])
            begin
                if( a[0]) y[3]= 1'b1;
                else y[2]= 1'b1;
            end
            else
            begin
                if(a[0]) y[1]= 1'bl;
                else y[0]= 1'b1;
            end
        end
    end
endmodule

上面这部分代码通过利用 if-else 语句描述每一种情况的条件, 同样实现一个了 3-8 译码器.

使用 case 语句

如果你觉得 if-else 语句还是不够直观, 你可以直接使用 case 语句来编写译码器. 仍然以 3-8 译码器为例

module dec38case(
    input [2:0] a,
    output reg [7:0] y
) ;
    always(*)
    begin
        case(a)
            3'd7: y = 8'b10000000;
            3'd6: y = 8'b01000000;
            3'd5: y = 8'b00100000;
            3'd4: y = 8'b00010000;
            3'd3: y = 8'b00001000;
            3'd2: y = 8'b00000100;
            3'd1: y = 8'b00000010;
            3'd0: y = 8'b00000001;
            default: y = 8" bxxxxxxxx
        endcase
    end
endmodule

使用 case 语句相比于使用 if-else 语句, 更加直观清晰.

本实验中总线译码器的描述方法

我们先以一个例子来看本次实验需要怎样的译码器.

image.png
实验三SoC结构图
上图是LAB3: "灯, 等灯, 等灯"-流水灯的几种点法中实现的SoC, 我们可以看到, SoC 的总线上外挂了四个外设, 分别为指令存储器(RAMCODE), 数据存储器(RAMDATA), 通过输入输出口(GPIO)以及流水灯(WATERLIGHT). 指令存储器的地址为 0x00000000 - 0x000000ff, 数据存储器的地址为 0x20000000 - 0x2000ffff, GPIO 的地址为 0x40000020, 0x40000024 和 0x40000028, 流水灯的地址为 0x40000000 - 0x4000000f.

从地址上我们可以看到, 如果我们想要对指令存储器进行一次操作, 只要保证地址的高 16bit 为 0x0000 即可. 如果我们想要对流水灯进行操作, 则需要确保地址线的高 28bit 为 0x4000000. 可以看到, 对不同外设进行操作时, 由于外设的有效地址长度不同(如指令存储器的有效长度为 0x0000 - 0xffff, 而流水灯的有效长度为0x0 - 0xf), 之前提到的几种译码器的描述方式均不是很适合本实验的译码器设计.

在附录的第一部分Verilog HDL基本语法中, 我们讲到了条件运算符. 我们可以使用条件运算符 ? : 进行译码. 在?后的表达式中写需要访问的外设的地址, : 后则为对应外设的使能端. 这样描述译码器只使用了 assign 语句, 简单直观, 同时避免了使用 always 语句描述组合逻辑时容易出现的毛刺等问题.

assign P0_HSEL = (HADDR[31:16] == 16'h0000) ? Port0_en : 1'b0; 
assign P1_HSEL = (HADDR[31:16] == 16'h2000) ? Port1_en : 1'b0; 
assign P3_HSEL = (HADDR[31:4] == 28'h4000000) ? Port3_en : 1'b0;
assign P4_HSEL = (HADDR[31:4] == 28'h4000002) ? Port4_en : 1'b0;

上面的四行代码展示了本次实验中的总线译码器的设计方法. 我们以第一行代码为例, 如果地址位的前 16 位为16'h0000, 则把 Port0_en (也就是指令存储器)的使能信号赋给片选信号, 代表总线现在对指令存储器进行操作. 之后的代码也是同理.

END

文章来源:

推荐内容

更多内容请关注微处理器系统结构与嵌入式系统设计专栏
推荐阅读
关注数
113
内容数
20
电子科技大学示范性微电子学院开设的「微处理器系统结构与嵌入式系统设计」课程配套实验,原链接:[链接]
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息