10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现

同步与异步清零

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第1张图片

就是当复位信号发生变化,从1到0时立刻进行复位,negedge触发模块,即可工作;但如果到0后一直没有发生变化,即保持为0,那么就是在不断的时钟上升沿触发电路,但是都会因为复位信号为0,使进行!rst_n的初始化操作,而不会进行正常工作。只有当复位信号重新变为1时,才会停止对电路的不断复位,进行电路的正常工作,直到复位信号再次从1到0,触发电路的复位

阻塞赋值与非阻塞

按先后顺序执行赋值语句,上面赋完值后下面再用就是新的值,而always里就是上面赋值,下面在赋值时,如果涉及到上面的值,是原值,而不是赋的新值进行操作,等到都结束了,才会统一都赋值

还有就是注意如果一个模块里有多个always,那么都是电路中的一部分,是电路中的不同部分,非阻塞赋值是要这个模块都结束后才赋值,而不是当前always结束后赋值,只有这个文件、板块里都结束了,才会把模块里的运算结果赋值给锁存器。

(那么前驱的定义在前在后其实是没有关系的,在采用非阻塞的情况下,因为前驱的更新是在当下前驱、参数都用完后才更新的)

例一

阻塞情况10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第2张图片

 非阻塞情况10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第3张图片

例二

阻塞,直接=的情况

用的都是现在的x1,x2,所以就可以直接由x1,x2连到各自的输出门上

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第4张图片<=的情况

此时g要用的是之前的x1,x2,所以如果f在g上,就需要用<=,来让之前的f传给g

要不就让g在f上,让g先变,再让f变,

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第5张图片可以发现,如果是=,各个输出信号就是直接出去的,没有再回来的

如果是<=,有输出信号是回来的,连回去的 

只能用非阻塞赋值描述时序电路,但是当 always 块中后面的赋值语句依赖于前面赋值 语句的结果时,非阻塞赋值会产生无意义的电路

例三

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第6张图片

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第7张图片

always块中对同一变量多次赋值,只保留最后一次赋值结果;意思不是说只执行最后一个非阻塞赋值,而是说自上而下,都执行,但是不断覆盖 ,注意用的值都是上一状态的,而不是在此时赋值阶段产生的值,即使自上而下过程中,某些量的值发生了变化,用的也还是之前的量,新产生的量再目前是不可见的

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第8张图片

 ROM实现

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第9张图片

深度为8就是说有8个地址,可以存储8个数,然后初始化为0~14,就是每个地址的初始值

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第10张图片这就是声明一个寄存器,可以保持数据锁存

首先要声明数据的存储空间,名称之前的表示每个数据有多少位,指位宽;之后的表示需要多少个数据,指深度.就是一共有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

 初始化完后为 

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第11张图片

   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

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第12张图片

主体电路的判断

在每个时钟上升沿工作

输出信号的初始化,置零

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第13张图片

主体的判断逻辑

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第14张图片

电路对未知情况的完善

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第15张图片

`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实现

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第16张图片

enb是读写模式的信号,addr是要读取或者覆写的地址信号,w_data是要写时写入的信号

在clk时工作,由于深度为128,所以用7位二进制数去表示地址,然后每位都是4位长

这个是声明128个寄存器,即深度为128,然后每个寄存器可以存储宽度为7的数

10.31同步异步清零,阻塞与非阻塞例子,ROM,RAM,边沿检测实现_第17张图片

循环的时候,要声明一个循环量,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;

你可能感兴趣的:(数电,java,开发语言)