算术逻辑单元
背景介绍
算术逻辑单元
算术逻辑单元(Arithmetic Logic Unit,下称 ALU)是一种对二进制整数执行算术运算(加法、减法等)或位运算(与、或、异或等)的组合逻辑数字电路。我们主要关注的信号是两个操作数输入、运算结果输出以及操作码输入,操作码输入主要是用来选择 ALU 进行运算的种类。除了上述端口,还有各类状态信号,比如进位信号、零信号、溢出信号、奇偶校验结果等,主要是对计算结果的特征进行说明。
全加器
全加器将两个二进制数 A, B 和一个进位输入 Cin 相加,得到一个和输出 S 以及一个进位输出 Cout。
一位全加器
根据一位全加器功能,很容易得到其真值表:

根据真值表,可以获得输出 S, Cout 关于输入 A, B, Cin 的函数:
\[
\begin{align}
    S &= A \oplus B \oplus C_{in} \\
    C_{out} &= AB + BC_{in} + AC_{in}
\end{align}
\]
行波加法器
将 N 个一位全加器“依次相连”来拓展位宽,可以得到 N 位行波加法器,其模式图如下:

超前进位加法器
行波加法器的组合时延会随着位宽拓展线性增加,其时延代价较大。使用超前进位加法器可以减少高位宽加法器的时延,感兴趣的同学可以查看超前进位加法器。
加减法器
在得到全加器实现后,可以为其添加一些逻辑实现加减法器。减法 A-B 可以看作 A+(-B),其中 -B 使用 B 的补码表示,即可使用全加器得到减法的结果。
加减法器实现
一位全加器
必须使用原理图完成
模块 Adder1b 必须使用原理图完成
 
根据背景介绍中得到的布尔表达式,我们可以使用以下电路图实现一位全加器:

请确保模块定义为:
 | module Adder1b(
    input A,
    input B,
    input Cin,
    output S,
    output Cout
)
  | 
 
四位加减法器
必须使用原理图完成
模块 AddSub4b 必须使用原理图完成
 
根据背景介绍,减数 B 的补码可以写作 ~B + 1。观察加法的 B 与减法的 -B,差别在于加法器第二个操作数是否取反以及是否有最低的进位,联想到异或操作的特性(一个操作数为 1 时,结果为另一个操作数的反;一个操作数为 0 时,结果与另一个操作数相同),可以得到以下原理图:
原理图

 
请确保模块定义为:
 | module AddSub4b(
    input [3:0] A,
    input [3:0] B,
    input       Ctrl,
    output[3:0] S,
    output      Cout
)
  | 
 
ALU 实现
本节将实现一个简单的 4 位 ALU,进行加减法、与、或操作。模块定义为:
 | module ALU(
    input [3:0] A,
    input [3:0] B,
/////// op: 0 for ADD; 1 for SUB; 2 for AND; 3 for OR ///////
    input [1:0] op,
    output[3:0] res,
    output      Cout // 0 when AND & OR
)
  | 
 
如果你希望通过原理图的方式实现,请自行绘制 And2b4 与 Or2b4 两个模块,模块功能为 4 位输入按位与 / 或,原理图如下:
原理图

 
请自行设计仿真文件,对 ALU 进行仿真,要求每个操作使用至少两组数据,需要注意对加减法的边界数据进行测试。
ALU 简单应用
去抖动模块
因为按键过程中会有机械原因导致的信号抖动,在上个实验中也可以观察到,一次按下按钮数字会跳动多次。我们可以实现一个去抖动模块,当信号稳定一段时间后才更改信号值。代码如下:
 | module pbdebounce(
    input wire clk,
    input wire button, 
    output reg pbreg
    );
    reg [7:0] pbshift;
    always@(posedge clk) begin
        pbshift = pbshift<<1;
        pbshift[0] = button;
        if (pbshift==8'b0)
            pbreg=0;
        if (pbshift==8'hFF)
            pbreg=1;    
    end
endmodule
  | 
 
七段数码管静态显示
我们希望使用 SWORD 板上的 8 个七段数码管,它接收串行数据以减少管脚使用。其实现已经提供在附件中。我们需要一个模块将 64 位的七段数码管数据转换为 1 位串行输出以及相应的同步信号,即并转串模块 P2S,因为后续实验中需要独立完成该模块,在本实验中只提供接口文件与用于综合的 EDIF 格式文件,你只需要将 P2S_io.v 和 P2S.edf 添加进工程即可,需要注意将工程中 P2S.edf 的文件格式设置为 EDIF(右键文件选择 Set file type)。
CreateNumber 模块
在本模块中使用 AddSub4b 模块,实现按键自增 / 自减。
new CreateNumber
 | /*
Module Name: CreateNumber
Description:
To change the value printed on Arduino using btns.
You will get a initial value printed as the para. INIT_HEXES defined.
After each pression on btn, a number will increase by 1.
This new module can handle i-1 when signal sw is 1
*/
module CreateNumber#(
parameter INIT_HEXES = 16'b1010_1011_1100_1101 // Init with "AbCd"
) (
input[3:0] btn,
input[3:0] sw,
output reg[15:0] num
);
wire[3:0] A, B, C, D;
initial num <= INIT_HEXES;
// D(the next num[3:0]) is always greater than current num[3:0] by 1
AddSub4b a0(.A(num[15:12]), .B(4'b0001), .Ctrl(sw[3]), .S(A));
AddSub4b a1(.A(num[11: 8]), .B(4'b0001), .Ctrl(sw[2]), .S(B));
AddSub4b a2(.A(num[ 7: 4]), .B(4'b0001), .Ctrl(sw[1]), .S(C));
AddSub4b a3(.A(num[ 3: 0]), .B(4'b0001), .Ctrl(sw[0]), .S(D));
// When pressing btn[0] num[3:0]++
always @(posedge btn[3]) num[15:12] <= A;
always @(posedge btn[2]) num[11: 8] <= B;
always @(posedge btn[1]) num[ 7: 4] <= C;
always @(posedge btn[0]) num[ 3: 0] <= D;
endmodule
  | 
 
 
顶层模块
本模块中调用的子模块说明:
clkdiv:时钟分频模块,直接使用 Lab7 提供的代码。 
pbdebounce, CreateNumber:使用本实验实验指导提供的代码。 
ALU:使用你在本实验中完成的模块。 
DisplayNumber:使用你在 Lab7 中完成的模块,并引入其子模块。 
Sseg_Dev:静态译码模块,使用附件中的模块。 
顶层模块代码:
Top
 | module Top(
        input wire clk,
        input wire [1:0] BTN,
        input wire [1:0] SW1,
        input wire [1:0] SW2,
        input wire [11:0] SW,
        output wire [3:0] AN,
        output wire [7:0] SEGMENT,
        output wire BTNX4,
        output wire seg_clk,
        output wire seg_clrn,
        output wire seg_sout,
        output wire SEG_PEN
);
    wire [15:0] num;
    wire [1:0] btn_out;
    wire [3:0] res;
    wire Co;
    wire [31:0] clk_div;
    wire [15:0] disp_hexs;
    wire [15:0] disp_hexs_my;
    assign disp_hexs[15:12] = num[7:4];            // B
    assign disp_hexs[11:8]  = num[3:0];            // A
    assign disp_hexs[7:4]   = {3'b000, Co};
    assign disp_hexs[3:0]   = res[3:0];                // C
    /* Code here */
    assign disp_hexs_my = (); // Fill the last four digits of your student id in ()
    assign BTNX4 = 1'b0;
    clkdiv m2(.clk(clk), .rst(0), .div_res(clk_div));
    pbdebounce m0(.clk(clk_div[17]), .button(BTN[0]), .pbreg(btn_out[0]));
    pbdebounce m1(.clk(clk_div[17]), .button(BTN[1]), .pbreg(btn_out[1]));
    CreateNumber m3(.btn({2'b0, btn_out}), .sw({2'b0, SW1}), .num(num)); // Attachment
    // The ALU module you wrote
    ALU m5(    .A(num[3:0]), 
                    .B(),                             // fill sth. in ()
                    .op(),                             // fill sth. in ()
                    .res(),                             // fill sth. in ()
                    .Cout(Co)); 
    // Module you design in Lab7
    DisplayNumber m6(    .clk(clk), .hexs(disp_hexs), .LEs(),         // fill sth. in ()
                                .points(SW[3:0]), .rst(),                             // fill sth. in ()
                                .AN(AN), .SEGMENT(SEGMENT));
    // Attachment
    SSeg_Dev m7(.clk(clk), .flash(clk_div[25]), .Hexs({disp_hexs_my, disp_hexs}), .LES(SW[11:4]),
                    .point({4'b0000, SW[3:0]}), .rst(1'b0), .Start(clk_div[20]), .seg_clk(seg_clk),
                    .seg_clrn(seg_clrn), .SEG_PEN(SEG_PEN), .seg_sout(seg_sout));
endmodule
  | 
 
 
你需要阅读代码并填写代码中的留空,请注意使得七段数码管高位(左边 4 个)显示你学号的后四位(比如学号 3220102333 的同学需要打印 2333)。
使用约束文件,生成比特流并下板进行验证。
实验报告要求
加减法器实现
- 模块 
Adder1b 原理图截图。 
- 模块 
AddSub4b 原理图截图。 
ALU 实现
- 模块 
ALU 原理图截图或代码。 
- 仿真文件代码,仿真波形与解释。
 
ALU 简单应用
- 完成后,截图工程中的文件结构,即 Lab4 中要求的截图。
 
- 拍摄下板现象,证明你实现了完整的功能。
 
思考题
请阅读 pddebounce 代码并分析以下几种情况,说明输出是否变化并解释,进而说明这一模块如何实现“去抖动”功能的:
- 
连续 8 个时钟正边沿到来时,button 为高电位。
 
- 
连续 8 个时钟正边沿到来时,button 为低电位。
 
- 
原输出 pbreg 为 0,得到的 button 输入如下图(数轴上的数字表示第几次时钟上边沿,0 时刻前输入均为 0)时刻 8 过后的输出为:

 
- 
如果改为下图,则时刻 8 过后的输出为:
