考研上微电子入了材料坑,开始自学verilog
此文很多也是在其他优秀的博文中复制过来
(在此也非常感谢大家在网上分享的笔记)
仅为方便记录自己学习的笔记
习题链接:HDLBits
本文大部分参考链接:https://www.cnblogs.com/LhTian/p/16608404.html
三,电路
3.1,组合逻辑电路
基本门电路
主要就是将抽象的问题,以真值表的形式表现出来。
- out_both: Each bit of this output vector should indicate whether both the corresponding input bit and its neighbour to the left (higher index) are '1'. For example, out_both[2] should indicate if in[2] and in[3] are both 1. Since in[3] has no neighbour to the left, the answer is obvious so we don't need to know out_both[3].
- out_any: Each bit of this output vector should indicate whether any of the corresponding input bit and its neighbour to the right are '1'. For example, out_any[2] should indicate if either in[2] or in[1] are 1. Since in[0] has no neighbour to the right, the answer is obvious so we don't need to know out_any[0].
- out_different: Each bit of this output vector should indicate whether the corresponding input bit is different from its neighbour to the left. For example, out_different[2] should indicate if in[2] is different from in[3]. For this part, treat the vector as wrapping around, so in[3]'s neighbour to the left is in[0]
(2023.10.19)
多路选择器
256- 1 4位多路复用器
Create a 4-bit wide, 256-to-1 multiplexer. The 256 4-bit inputs are all packed into a single 1024-bit input vector. sel=0 should select bits in[3:0], sel=1 selects bits in[7:4], sel=2 selects bits in[11:8], etc.
module top_module(
input [1023:0] in,
input [7:0] sel,
output [3:0] out );
assign out = {in[sel*4+3],in[sel*4+2],in[sel*4+1],in[sel*4]};
or
assign out = in[sel*4 +: 4];
endmodule
assign out = in[sel*4 +: 4];
in[sel*4 +: 4]
"+:" 相当于"+="
sel*4选定了起始,然后从起始开始每次加4,4个取为一组
in[sel*4+3 -: 4]; 也是一样的道理
特别的 in[ sel*4+3 : sel*4 ],起始和终点都随着sel变化,这是不允许的,verilog中只允许起始或终点发生变化,其他保持常量,这样做是为了保证宽度不发生改变
这道题,因为位宽过大,用case显然不适合,官网给的提示也是说用case不合适,采用了矢量分割的方式来解决这道题目,因为矢量的索引是可以变化的,前提是合成器能够计算出所选位的宽度是恒定的。并且官网还提示说“An error saying “… is not a constant” means it couldn’t prove that the select width is constant. In particular, in[ sel*4+3 : sel*4 ] does not work.”这种写法是不正确的,应该避免。
运算电路
有符号加法溢出
eg:Assume that you have two 8-bit 2's complement numbers, a[7:0] and b[7:0]. These numbers are added to produce s[7:0]. Also compute whether a (signed) overflow has occurred.
当两个正数相加产生负结果,或两个负数相加产生正结果时,会发生有符号溢出。检测溢出有几种方法:可以通过比较输入和输出数的符号来计算溢出,也可以通过n和n-1位的执行来计算溢出。
module top_module (
input [7:0] a,
input [7:0] b,
output [7:0] s,
output overflow
);
assign s = a+b;
assign overflow = (~a[7]&~b[7]&s[7])|(a[7]&b[7]&~s[7]);
endmodule
1、补码加法与减法
符号位和数值位一起参与运算,并且结果就是最终结果(包括符号位与数值位)
[x]补 + [y]补 = [x+y]补 两数补码的和等于两数和的补码
[x]补 - [y]补 = [x]补 + [-y]补 = [x-y]补
2、溢出与进位
1)溢出指带符号数的补码运算溢出,用来判断带符号数补码运算结果是否超出补码所能表示的范围。当两个正数相加产生负结果,或两个负数相加产生正结果时,会发生有符号溢出
2)进位指运算结果的最高位向更高位的进位,用来判断无符号数运算结果是否超出了计算机所能表示的最大无符号数的范围
3、判断溢出的方法
1)利用符号位和数值部分的最高位的进位状态来判断结果是否溢出,通过该两位进位状态的异或结果来判断,结果为1时,表示溢出,结果为0时,表示没有溢出
2)通过参与运算的两个数的符号及运算结果的符号进行判断
加法器
1. 可以直接由简单的逻辑门生成
1) sum 与输入的关系是异或
2) cout与输入的关系是逻辑与
2. 可以由位拼接符+assign语句的方式直接得到(半加器,全加器)
比如{cout,sum} = a+b
需要注意的是cout在高位,假设a,b,sum都是2位宽的
a = 011,b=110 则a+b = 1 001
当使用{}时,cout=1,sum=001
3. 对于多位的加法器,可以通过实例化半加器或者全加器,根据需要进行连线
(2023.10.24)
卡诺图化简
- SOP form 即与或式,对应于卡诺图就是圈1即可;
- POS form 即或与式,对应于卡诺图就是圈0后,整体取反;
3.2,时序逻辑电路
触发器
D触发器flip-flops
创建一个D触发器,根据数电的知识知道,输出q=d(这里是clk的上升沿时刻),用时序逻辑进行一个编写,这里需要注意的是如果使用时序逻辑进行编写的话,需要用非阻塞赋值的方式
module top_module (
input clk, // Clocks are used in sequential circuits
input d,
output reg q );//
always@(posedge clk)begin
q <=d;
end
endmodule
创建一个高电平有效复位的同步D触发器,reset高电平时复位,低电平时赋值d
module top_module (
input clk,
input reset, // Synchronous reset
input [7:0] d,
output [7:0] q
);
always@(posedge clk)begin
if(reset==0)
q <=d;
else
q <=0;
end
endmodule
posedge上升沿触发
negedge下降沿触发
reset高电平时复位,低电平时赋值d
resetn低电平时复位,高电平时赋值d
(2023.10.25)
//在Verilog中,灵敏度列表看起来很奇怪。FF的重置对areset的“水平”很敏感,那么为什么使用“posedge areset”工作呢?
//要了解其工作原理,请考虑所有改变输入信号的事件的真值表,假设clk和areset没有精确地同时切换:
// clk areset output
// x 0->1 q <= 0; (because areset = 1)
// x 1->0 no change (always block not triggered)
// 0->1 0 q <= d; (not resetting)
// 0->1 1 q <= 0; (still resetting, q was 0 before too)
// 1->0 x no change (always block not triggered)
建立一个异步复位的D触发器
module top_module (
input clk,
input d,
input ar, // asynchronous reset
output q);
always@(posedge clk or posedge ar)begin
if(ar==1)
q = 0;
else
q <= d;
end
endmodule
建立一个同步复位的D触发器
module top_module (
input clk,
input d,
input r, // synchronous reset
output q);
always@(posedge clk)begin
if(r==1)
q <= 0;
else
q <= d;
end
endmodule
D锁存器latch
锁存器是电平敏感(非边缘敏感)电路,因此在always块中,它们使用电平敏感灵敏度列表。
但是,它们仍然是顺序元素,因此应该使用非阻塞赋值。
d锁存器在启用时充当导线(或非反相缓冲器),在禁用时保留当前值。
module top_module (
input d,
input ena,
output q);
always@(posedge d or posedge ena)begin
if(ena==1)
q <= d;
else
q <= q;
end
endmodule
D触发器与门电路
module top_module (
input clk,
input in,
output out);
wire d;
assign d = out^in;
always@(posedge clk)begin
out <= d;
end
endmodule
module top_module (
input clk,
input x,
output z
);
endmodule
D触发器转JK触发器
D触发器的逻辑功能为;
JK触发器的逻辑功能为;
则。
module top_module (
input clk,
input j,
input k,
output Q);
wire d;
assign d=(j&~Q)|(~k&Q);
always@(posedge clk)begin
Q <=d;
end
endmodule
(2023.10.30)
https://blog.csdn.net/weixin_45931009/article/details/116380969?spm=1001.2014.3001.5502
1.如何获取信号的沿
首先对想要采集沿的信号(假设是in)进行打一拍的操作存放到临时寄存器中,假设是in_reg 则:
获取上升沿:in &~ in_reg
获取下降沿:in_reg &~ in
获取双边沿:in ^ in_reg
2.同步复位与异步复位
同步复位:指复位信号在时钟信号触发时,复位才有效
异步复位:指复位信号不管时钟信号是否有触发,只有复位有效,输出立刻变为0
写法:
/*同步复位*/
always @(posedge clk) begin
if (rst == 1'b? )
...
end
/*异步复位*/
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0)
...
end
3.构建一个在功能上表现为双边缘触发触发器的电路
// After posedge clk, p changes to d^n. Thus q = (p^n) = (d^n^n) = d.
// After negedge clk, n changes to p^n. Thus q = (p^n) = (p^p^n) = d.
// At each (positive or negative) clock edge, p and n FFs alternately
// load a value that will cancel out the other and cause the new value of d to
(2023.11.08)