同步与异步清零
就是当复位信号发生变化,从1到0时立刻进行复位,negedge触发模块,即可工作;但如果到0后一直没有发生变化,即保持为0,那么就是在不断的时钟上升沿触发电路,但是都会因为复位信号为0,使进行!rst_n的初始化操作,而不会进行正常工作。只有当复位信号重新变为1时,才会停止对电路的不断复位,进行电路的正常工作,直到复位信号再次从1到0,触发电路的复位
阻塞赋值与非阻塞
按先后顺序执行赋值语句,上面赋完值后下面再用就是新的值,而always里就是上面赋值,下面在赋值时,如果涉及到上面的值,是原值,而不是赋的新值进行操作,等到都结束了,才会统一都赋值
还有就是注意如果一个模块里有多个always,那么都是电路中的一部分,是电路中的不同部分,非阻塞赋值是要这个模块都结束后才赋值,而不是当前always结束后赋值,只有这个文件、板块里都结束了,才会把模块里的运算结果赋值给锁存器。
(那么前驱的定义在前在后其实是没有关系的,在采用非阻塞的情况下,因为前驱的更新是在当下前驱、参数都用完后才更新的)
例一
阻塞情况
非阻塞情况
例二
阻塞,直接=的情况
用的都是现在的x1,x2,所以就可以直接由x1,x2连到各自的输出门上
<=的情况
此时g要用的是之前的x1,x2,所以如果f在g上,就需要用<=,来让之前的f传给g
要不就让g在f上,让g先变,再让f变,
可以发现,如果是=,各个输出信号就是直接出去的,没有再回来的
如果是<=,有输出信号是回来的,连回去的
只能用非阻塞赋值描述时序电路,但是当 always 块中后面的赋值语句依赖于前面赋值 语句的结果时,非阻塞赋值会产生无意义的电路
例三
always块中对同一变量多次赋值,只保留最后一次赋值结果;意思不是说只执行最后一个非阻塞赋值,而是说自上而下,都执行,但是不断覆盖 ,注意用的值都是上一状态的,而不是在此时赋值阶段产生的值,即使自上而下过程中,某些量的值发生了变化,用的也还是之前的量,新产生的量再目前是不可见的
ROM实现
深度为8就是说有8个地址,可以存储8个数,然后初始化为0~14,就是每个地址的初始值
这就是声明一个寄存器,可以保持数据锁存
首先要声明数据的存储空间,名称之前的表示每个数据有多少位,指位宽;之后的表示需要多少个数据,指深度.就是一共有8个寄存器,每个寄存器可以存储宽度为4的数据
reg [3:0] rom_data [7:0];
//保持ROM中的数据不变
always @(posedge clk or negedge rst_n)
if (!rst_n) //对ROM中的数据进行初始化
begin
rom_data[0] <= 4'd0;
rom_data[1] <= 4'd2;
rom_data[2] <= 4'd4;
rom_data[3] <= 4'd6;
rom_data[4] <= 4'd8;
rom_data[5] <= 4'd10;
rom_data[6] <= 4'd12;
rom_data[7] <= 4'd14;
end
else
begin //保持ROM中的数据不变
rom_data[0] <= 4'd0;
rom_data[1] <= 4'd2;
rom_data[2] <= 4'd4;
rom_data[3] <= 4'd6;
rom_data[4] <= 4'd8;
rom_data[5] <= 4'd10;
rom_data[6] <= 4'd12;
rom_data[7] <= 4'd14;
end
初始化完后为
always @(posedge clk or negedge rst_n)
if (!rst_n)
data <= 4'd0;
else
data <= rom_data[addr];
之前理解的是模块声明与调用完后,所调用的内存与元件就都被销毁了,和函数一样,
但实际上模块和语言里的函数是不同的,是电路组装的,模块调用不会终止,只要电路工作就会一直存在,在语句里声明的reg,wire都会一直存在,一直工作,而不是函数里那种自上而下,用完即销毁的。 保持工作状态,就是看时钟沿
在每次使用完后,寄存器里的数据也会进行保持,而不是被销毁,就是声明和定义只有一次,但是赋值是不断进行的,即每次电路功能是从assign与always开始的,而不是从模板下的定义开始的
也可以理解成只是声明,声明有这么一个东西和信号,后面要用到,而不是说在这里去定义出来
定义和赋值是相关的,用到哪些,最后电路就生成哪些,而不是看声明的
`timescale 1ns/1ns
module rom(
input clk,
input rst_n,
input [7:0]addr,
output [3:0]data
);
reg [3:0] rom_data [7:0];
assign data = rom_data[addr];
//保持ROM中的数据不变
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
rom_data[0] <= 4'd0;
rom_data[1] <= 4'd2;
rom_data[2] <= 4'd4;
rom_data[3] <= 4'd6;
rom_data[4] <= 4'd8;
rom_data[5] <= 4'd10;
rom_data[6] <= 4'd12;
rom_data[7] <= 4'd14;
end
else
begin
rom_data[0] <= rom_data[0];
rom_data[1] <= rom_data[1];
rom_data[2] <= rom_data[2];
rom_data[3] <= rom_data[3];
rom_data[4] <= rom_data[4];
rom_data[5] <= rom_data[5];
rom_data[6] <= rom_data[6];
rom_data[7] <= rom_data[7];
end
endmodule
核心就是声明一个锁存器,然后根据输入的地址信号,从锁存器里取出来锁存的信号,用assign语句,always里主要就是复位以及对锁存状态的一个保持(这个可有可无) 。
只读不写
信号边沿检测
目的是为了检测目标信号发生变化时,是上升还是下降
检测信号a的边沿需要缓存信号前一时刻的值,例如记为a_tem。当前一时刻为0,这一时刻为1,说明信号出现上升沿,即 a&&!a_tem = 1; 当前一时刻为1,这一时刻为0,说明信号出现下降沿,即 !a&&a_tem = 1;
即如果下降沿,那么前一个前状态一定是1,现在状态一定是0;上升沿,前一定是0,现在一定是1;0的取反,然后和1的与出来就是1
前驱a的初始化与赋值
在每个时钟上升沿工作
声明为锁存,一方面是always的必须要求,另一方面是说,如果是wire就直接输出不做保存了,但是前驱a需要时刻保存上一个时钟上升沿(电路工作)时a的值,而不是任意时刻a的值,所以声明为reg,而不是wire
主体电路的判断
在每个时钟上升沿工作
输出信号的初始化,置零
主体的判断逻辑
电路对未知情况的完善
`timescale 1ns/1ns
module edge_detect(
input clk,
input rst_n,
input a,
output reg rise,
output reg down
);
reg a_tem;
//缓存a的数值
always @(posedge clk or negedge rst_n)
if (!rst_n)
a_tem <= 1'b0;
else
a_tem <= a;
//检测边沿,给出相应的信号
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
rise <= 1'b0;
down <= 1'b0;
end
else if (!a_tem && a) //当前一时刻a=0,当前时刻a=1,表示a出现一次上升沿
begin
rise <= 1'b1;
down <= 1'b0;
end
else if (a_tem && !a) //当前一时刻a=1,当前时刻a=0,表示a出现一次下降沿
begin
down <= 1'b1;
rise <= 1'b0;
end
else
begin
down <= 1'b0;
rise <= 1'b0;
end
endmodule
单端口RAM实现
enb是读写模式的信号,addr是要读取或者覆写的地址信号,w_data是要写时写入的信号
在clk时工作,由于深度为128,所以用7位二进制数去表示地址,然后每位都是4位长
这个是声明128个寄存器,即深度为128,然后每个寄存器可以存储宽度为7的数
循环的时候,要声明一个循环量,integer i,
复位的时候让128每个寄存器都为0,都遍历到
正常工作就是让对应地址(二进制转化的实际意义)上的数为输入的数据
最后的输出,如果enb是1,就是写,那么就不输出;是0,就是读,就输出
数据操作与赋值时,不用管位宽,它就是用来衡量数据的大小,和int是一个意思,如果不具体声明宽度中的某一位时,它操作时就是按照其实际意义来运算的。
`timescale 1ns/1ns
module RAM_1port(
input clk,
input rst,
input enb,
input [6:0]addr,
input [3:0]w_data,
output wire [3:0]r_data
);
reg[3:0] ram[127:0];
integer i;
always@(posedge clk,negedge rst)begin
if(!rst)begin
for(i=0;i<127;i=i+1)begin
ram[i]='b0;
end
end
else if(enb)begin
ram[addr]=w_data;
end
end
assign r_data=(!enb)?ram[addr]:'b0;