鄙人能力有限,对数电的理解还远远不够,甚至在基础知识上也有问题。于是写下此篇post,加深对数电的理解,并且结合verilog代码。在代码部分,我和同伴共同创作,会使用ChatGPT进行检测。注意:若代码有问题,请及时提出自己的疑问或是指出我的错误,相互学习共同进步,十分感谢!
三种基本运算 与门 表达式与真值表
表达式 $$ Y=A\cdot B $$
真值表
A
B
Y
0
0
0
0
1
0
1
0
0
1
1
1
verilog代码 1 2 3 4 5 6 7 module and_gate ( input a, input b, output c ); assign c = a & b; endmodule
或门 表达式与真值表
表达式 $$ Y=A + B $$
真值表
A
B
Y
0
0
0
0
1
1
1
0
1
1
1
1
verilog代码 1 2 3 4 5 6 7 module or_gate ( input a, input b, output c ); assign c = a | b; endmodule
非门 表达式与真值表
表达式 $$ Y=\bar{A} $$
真值表
verilog代码 1 2 3 4 5 6 7 module not_gate ( input a, output b ); assign b = ~a; #assign b = ! a这样写也可以 endmodule
与非 表达式与真值表
表达式 $$ Y=\overline{(A\cdot B) } $$
真值表
A
B
Y
0
0
1
0
1
1
1
0
1
1
1
0
verilog代码 1 2 3 4 5 6 7 8 9 10 11 12 module nand_gate ( input wire A, input wire B, output wire Y ); wire and_out; and (and_out, A, B); not (Y, and_out); endmodule
或非 表达式与真值表
表达式 $$ Y=\overline{(A + B) } $$
真值表
A
B
Y
0
0
1
0
1
0
1
0
0
1
1
0
verilog代码
结构化结构
1 2 3 4 5 6 7 8 9 10 11 12 module nor_gate ( input wire A, input wire B, output wire Y ); wire or_out; or (or_out, A, B); not (Y, or_out); endmodule
数据流描述
1 2 3 4 5 6 7 8 9 10 module nor_gate ( input wire A, input wire B, output wire Y ); assign Y = ~(A | B); endmodule
与或非 表达式与真值表
表达式 $$ Y=\overline{(A\cdot B+C\cdot D)} $$
真值表
A
B
C
D
Y
0
0
0
0
1
0
0
0
1
1
0
0
1
0
1
0
0
1
1
0
0
1
0
0
1
0
1
0
1
1
0
1
1
0
1
0
1
1
1
0
1
0
0
0
1
1
0
0
1
1
1
0
1
0
1
1
0
1
1
0
1
1
0
0
0
1
1
0
1
0
1
1
1
0
0
1
1
1
1
0
verilog代码
结构化结构
1 2 3 4 5 6 7 8 9 10 11 12 module nand_gate ( input wire A, input wire B, output wire Y ); wire and_out; and (and_out, A, B); not (Y, and_out); endmodule
数据流描述
1 2 3 4 5 6 7 8 9 10 module nand_gate ( input wire A, input wire B, output wire Y ); // 直接用数据流描述与或非门的逻辑功能 assign Y = ~(A & B); endmodule
异或 表达式与真值表
表达式 $$ Y = A\oplus B$$
真值表
A
B
Y
0
0
0
0
1
1
1
0
1
1
1
0
verilog代码
结构化结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 module xor_gate ( input wire A, input wire B, output wire Y ); wire and1, and2, notA, notB; not (notA, A); not (notB, B); and (and1, A, notB); and (and2, notA, B); or (Y, and1, and2); endmodule
数据流描述
1 2 3 4 5 6 7 8 module xor_gate ( input wire A, input wire B, output wire Y ); assign Y = A ^ B; endmodule
同或 表达式与真值表
表达式 $$ Y = A\odot B $$
真值表
A
B
Y
0
0
1
0
1
0
1
0
0
1
1
1
verilog代码
结构化结构
1 2 3 4 5 6 7 8 9 10 11 12 module xnor_gate ( input wire A, input wire B, output wire Y ); wire xor_out; xor (xor_out, A, B); not (Y, xor_out); endmodule
数据流描述
1 2 3 4 5 6 7 8 9 module xnor_gate ( input wire A, input wire B, output wire Y ); assign Y = ~(A ^ B); endmodule
组合逻辑电路 编码器 代码 我举一个4-2编码器的代码:
1 2 3 4 5 6 7 8 9 module bianma42( input [3:0] i_dec, output [1:0] o_dec ) assign o_dec = (i_dec : 4'b0001) ? 2'b00: (i_dec = 4'b0010) ? 2'b01: (i_dec = 4'b0100) ? 2'b10: (i_dec = 4'b1000) ? 2'b11 : 2'bxx; endmodule
译码器 3-8译码器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 module decoder3to8 ( input wire [2:0] in, input wire enable, output reg [7:0] out ); always @(*) begin if (enable) out = 8'b00000001 << in; else out = 8'b00000000; end endmodule
加法器 8位加法器
1 2 3 4 5 6 7 module adder ( input [7:0] a, input [7:0] b, output [7:0] sum ); assign sum = a + b; endmodule
乘法器 8位乘法器
1 2 3 4 5 6 7 module multiplier ( input [7:0] a, input [7:0] b, output [15:0] product ); assign product = a * b; endmodule
选择器 4选1
1 2 3 4 5 6 7 8 9 10 11 12 module mux4to1 ( input [7:0] a, input [7:0] b, input [7:0] c, input [7:0] d, input [1:0] sel, output [7:0] out ); assign out = (sel == 2'b00) ? a : (sel == 2'b01) ? b : (sel == 2'b10) ? c : d; endmodule
比较器 1 2 3 4 5 6 7 module comparator ( input [7:0] a, input [7:0] b, output equal ); assign equal = (a == b); endmodule
移位器 1 2 3 4 5 6 7 module shifter ( input [7:0] in, input [2:0] shamt, output [7:0] out ); assign out = in << shamt; endmodule
时序逻辑 寄存器 在always结构化过语句后面增加边缘敏感信号(posedge和negedge),就会变成一个简单的寄存器!
简单寄存器 寄存器的作用是让能够存储一组二值(0和1)代码,当边缘敏感信号触发时,就会收集收到的数据。以下是代码:
1 2 3 4 5 6 7 8 module dff( input i_clk, input i_din, output reg o_dout ); always@(posedge i_clk) o_dout <= i_din; endmodule
同步有限状态机 计数器 计数器是一个非常简单的时序逻辑。计数器在系统时钟触发下进行计数,并在达到某一个值的时候输出对应的触发信号给其他的电路模块。
此处用一个4位宽的计数器做例子。一开始的初始值为0(verilog代码中可写作4’b0000),这时候我们启动时钟,每上升(或下降)一次就敲一下计数器,计数器就会记仇并在他的小本子上记录你欺负他的次数。由于是4位宽,所以他的忍耐上线是15次(verilog代码4’b1111)。当你达到他忍耐的上线时,他就会去告状并且发出一个忍无可忍的信号o_full,等他火气消了初始值又回变成0。计数器还包含了一个使能信号,当使能信号无效时,计数器暂停计数。
代码可以这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module counter_4( input i_clk, input i_rst, input i_en, output reg [3:0] o_cnt, output o_full ) always@(posedge i_clk or posedge i_rst)begin if(i_rst) o_cnt <= 4'b0000; else if(i_en) o_cnt <= o_cnt + 4'b0001; end assign o_full = (o_cnt == 4'b1111); endmodule
时钟分频器 时钟分频器的作用是将高频率的时钟转换为低频率的时钟,如果你看过这个视频”计数器 “更能够理解分频。
时钟分频计数器可以分为两类,一类是偶数倍分频 ,如视频所述的那样用计数器就可以实现,且分频后时钟的占空比可以达到50%;另一类则是奇数倍分频 ,是不能达到50%的。
如果你硬是要挑战难度,想用奇数倍分频达到50%,则需要同时利用输入时钟的上、下沿进行计数,产生两个奇数分频器,然后做异或运算就可以得到
偶数倍分频器 设计一个偶数倍时钟分频器,该电路将输入时钟进行分频,产生一个相位与源时钟相同、周期为8的信时钟。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 module even_freq_div( input i_clk, input i_rst_n, output reg o_clk ); reg [2:0] cnt; // 3位计数器,能计数到7 always @(posedge i_clk or negedge i_rst_n) if (!i_rst_n) cnt <= 3'b000; else if (cnt == 3'b111) cnt <= 3'b000; else cnt <= cnt + 1; always @(posedge i_clk or negedge i_rst_n) if (!i_rst_n) o_clk <= 1'b0; else if (cnt == 3'b111) o_clk <= ~o_clk; endmodule
奇数倍分频器 设计一个占空比不为50%的奇数倍分频器,该电路的输出时钟周期为输入时钟的7倍,占空比为4/7。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 module odd_divider_7_4( input clk, input rst, output reg clk_out ); reg [2:0] counter; always @(posedge clk or posedge rst) begin if (rst) begin counter <= 3'd0; end else begin if (counter == 3'd6) begin counter <= 3'd0; // 计数器到达7时复位 end else begin counter <= counter + 3'd1; end end end always @(posedge clk or posedge rst) begin if (rst) begin clk_out <= 1'b0; end else begin if (counter < 3'd4) begin clk_out <= 1'b1; // 在0到3时输出高电平 end else begin clk_out <= 1'b0; // 在4到6时输出低电平 end end end endmodule