总线
- 总线(Bus),一路信号
- 使用条件运算符(?:),实现三态缓冲器(Tristate Buffer)
- 如果控制信号为1,那么输出信号等于输入信号
- 如果控制信号为0,那么输出信号为高阻态,类似于断开连接
- 在Verilog中,z表示高阻态,x表示未知
- 输入信号为四路信号SW[3:0],控制信号为四路信号SW[7:4],总线信号为一路信号LEDR[0]
// ----- Bus -----
// (SW) -> (LEDR)
module TopLevel(SW, LEDR);
input [9:0] SW;
output [9:0] LEDR;
assign LEDR[0] = SW[4] ? SW[0] : 1'bz;
assign LEDR[0] = SW[5] ? SW[1] : 1'bz;
assign LEDR[0] = SW[6] ? SW[2] : 1'bz;
assign LEDR[0] = SW[7] ? SW[3] : 1'bz;
endmodule
复用器、解复用器
- 复用器(Multiplexer,Mux),多路信号转一路信号;解复用器(Demultiplexer,Demux),一路信号转多路信号
- 使用case语句,实现译码器(Decoder)
- 上面的总线需要4个开关对应4种情形,并且可能选中多种情形,产生信号冲突
- 译码器可以减少开关数量(2个开关对应4种情形)、防止信号冲突(只能选中1种情形)。因此,译码器通常用于选中1种情形
- 信号转换
- Mux通常用于多个设备连接到一个总线,比如四路信号SW[3:0],转一路信号LEDR[0]
- Demux通常用于一个总线连接到多个设备,比如一路信号KEY[0],转四路信号LEDR[3:0]
- 按钮KEY[0]未按下时为1,按下时为0,所以我们需要进行非运算(~)
// ----- Mux -----
// (SW) -> (LEDR)
module TopLevel(SW, LEDR);
input [9:0] SW;
output [9:0] LEDR;
reg mux_signal;
assign LEDR[0] = mux_signal;
always @(*)
case (SW[5:4])
0: mux_signal = SW[0];
1: mux_signal = SW[1];
2: mux_signal = SW[2];
3: mux_signal = SW[3];
endcase
endmodule
// ----- Demux -----
// (SW, KEY) -> (LEDR)
module TopLevel(SW, KEY, LEDR);
input [9:0] SW;
input [1:0] KEY;
output [9:0] LEDR;
reg [9:0] demux_signal;
assign LEDR = demux_signal;
always @(*)
case (SW[1:0])
0: demux_signal[0] = ~KEY[0];
1: demux_signal[1] = ~KEY[0];
2: demux_signal[2] = ~KEY[0];
3: demux_signal[3] = ~KEY[0];
endcase
endmodule
寄存器
- 寄存器(Register),存储数据
- 输入信号A[3:0]、B[3:0],进行按位与,结果存入寄存器C[3:0]
- wire变量和reg变量
- wire变量表示导线,输入信号改变,则输出入信号改变
- reg变量表示寄存器,输入信号改变,则只有敏感列表的事件发生后,输出信号改变
- 敏感列表的事件为posedge(~KEY[0]),即~KEY[0]的上升沿
- ~KEY[0],0 –> 按下按钮 –> 1 –> 放开按钮 –> 0
- ~KEY[0]的上升沿为按下按钮,此时结果存入寄存器C[3:0]
- 通常,我们使用一个固定时钟信号的上升沿。因此,寄存器的数据变化,必须和时钟信号的周期变化同步,这样的数字电路称为同步电路
- 同步电路使用非阻塞赋值(<=)
- CPU的频率,即为时钟频率,它反映了CPU的运算速度。比如CPU的频率为4GHz,1秒可以运算4G = 40亿次
// ----- Register -----
// (SW, KEY) -> (LEDR)
module TopLevel(SW, KEY, LEDR);
input [9:0] SW;
input [1:0] KEY;
output [9:0] LEDR;
wire [3:0] A, B;
reg [3:0] C;
assign A = SW[3:0];
assign B = SW[7:4];
assign LEDR[3:0] = C;
always @(posedge(~KEY[0]))
C <= A & B;
endmodule
算术逻辑单元
- 算术逻辑单元(Arithmetic Logical Unit,ALU)
- 使用NAND,实现RS锁存器(RS Latch)
- RS锁存器是一种基于NAND的存储器。类似的还有D触发器(D Flip Flop),它在信号边沿触发数据改变,通常用于实现寄存器。关于基于NAND的存储器,可参见SSD架构和PCIe接口
- w0,x –> 按下KEY[1] –> 0 –> 按下KEY[0] –> 1
- 我们先按下KEY[1],再按下KEY[0],提供一个时钟信号clk的上升沿
- CPU的算术运算、逻辑运算是在ALU中实现的
- ALU使用一个译码器,将4位操作码opcode译为16种情形
- ALU的第一个操作数为6位寄存器R,第二个操作数为6位op2,结果存入6位寄存器R
- 这里,opcode和ARM指令集一致。因此,它可以作为实现ARM CPU的基础
- 有了基于ALU的CPU,以及七段数码管的显示器,我们已经可以实现一个口袋计算器。关于口袋计算器,可参见液晶显示器的发展历程
// ----- Arithmetic Logical Unit (ALU) -----
* TopLevel()
* ALU()
// (SW, KEY) -> (LEDR)
module TopLevel(SW, KEY, LEDR);
input [9:0] SW;
input [1:0] KEY;
output [9:0] LEDR;
// RS latch as clock signal
wire w0, w1;
nand (w0, KEY[0], w1);
nand (w1, KEY[1], w0);
ALU (SW[9:6], SW[5:0], w0, LEDR[5:0]);
endmodule
// (opcode, op2, clk) -> (result)
module ALU(opcode, op2, clk, result);
input [3:0] opcode;
input [5:0] op2;
input clk;
output [5:0] result;
reg [5:0] R;
assign result = R;
always @(posedge(clk))
case (opcode)
0: R <= R & op2; // AND
1: R <= R ^ op2; // EOR (Exclusive OR)
2: R <= R - op2; // SUB
3: R <= op2 - R; // RSB (Reverse SUB)
4: R <= R + op2; // ADD
12: R <= R | op2; // ORR (Inclusive OR)
13: R <= op2; // MOV
14: R <= R & ~op2; // BIC (Bit Clear)
15: R <= ~op2; // MVN (Move NOT)
default: R <= 0; // None of above
endcase
endmodule