前言

刚学前端设计的时候,听到的就是组合逻辑、时序逻辑,很重要!但是究竟有什么用?到底怎么体现,没有多少老师可以明确指出来,当自己看的东西多了,就可以理解了,甚至可以得出自己的范式。

到目前为止,要想掌握组合逻辑,就请先掌握本文列出的计数器、触发器、锁存器、寄存器分频器等简单的组合逻辑电路。

1.触发器

包括RS触发器、JK触发器、D触发器、T触发器。

2.锁存器和寄存器

锁存器的功能同触发器类似,但也有本质区别:触发器是在有效时钟沿到来时才发生作用,而锁存器是电平敏感的,只要时钟信号有效,锁存器就会起作用

2.1锁存器

2.1.1电平敏感的1位数据锁存器

module latch1(
	clk,
	d,
	q
);
	input clk,d;
	output q;
	
	assign q = clk?d:q;

endmodule

综合的电路图如下:

在这里插入图片描述

2.1.2带有置位功能和复位功能的电平敏感的1位数据锁存器

module latch2(
	clk,load,reset,d,q
);
input clk,load,reset,d;
output q;

assign q = reset?1'b0:(load?1'b1:(clk?d:q));

endmodule
综合得到的电路图如下:

在这里插入图片描述

2.2 寄存器

推荐阅读FPGA应该掌握的小笔记中涉及到的寄存器知识点。

带有清零功能的8位数据寄存器reg_8.v

推荐阅读:异步复位清零的一些常识

module reg_8(
	out,
	in,
	clk,
	clr
);
	output 	[7:0] out;
	input		[7:0]	in;
	input				clk;
	input				clr;
	reg		[7:0]	out;
	
	always@(posedge clk or posedge clr)
	if(clr)		out <= 0;
	else			out <= in;
endmodule
综合得到的电路图如下:

在这里插入图片描述

3.移位寄存器

3.1 8位左移移位寄存器shiftleft_reg.v

module shiftleft_reg(clk,rst,l_in,s,q);
	input clk,rst,l_in,s;
	output 	[7:0]	q;
	reg		[7:0]	q;
	always@(posedge clk)
	begin
		if(rst)
			q <= 8'b0;
		else if(s)
			q <= {q[6:0],l_in};
		else 
			q <= q;
	end

endmodule

3.2 8位右移移位寄存器

module shiftright_reg(clk,rst,r_in,s,q);
	input clk,rst,r_in,s;
	output 	[7:0]	q;
	reg		[7:0]	q;
	always@(posedge clk)
	begin
		if(rst)
			q <= 8'b0;
		else if(s)
			q <= {r_in,q[7:1]};
		else 
			q <= q;
	end

endmodule

4.分频器

5.计数器

6.其他时序逻辑电路

6.1同步器

当一个时序电路的输入由另一个时钟驱动的电路产生或来自一个外部异步电路时,需要用同步器将输入数据与需要的时钟同步。即常用在跨时钟处理!

简单同步器设计代码Synchronizer.v

module Synchronizer(
	clk,
	data,
	syn
);

input 	clk;
input 	data;
output	syn;
reg 		syn;

always@(posedge clk)
	if(data == 0)
		syn <= 0;
	else 
		syn <=1;

endmodule

仿真文件Synchronizer_tb.v

`timescale 1ns/1ns
`define clk_period 20

module Synchronizer_tb;

	reg 	clk;
	reg 	data;
	wire	syn;

	Synchronizer Synchronizer_inst(
		clk,
		data,
		syn
	);

	initial clk = 1;
	always #(`clk_period/2) clk = ~clk;

	initial begin
		data = 1'b0;
		#(`clk_period*2+5);
		data = 1'b1;
		#(`clk_period*2);
		data = 1'b0;
		#(`clk_period*2+6);
		data = 1'b1;
		#8;
		data = 1'b0;
		#16;
		data = 1'b1;
		#(`clk_period);
		data = 1'b0;
		#(`clk_period*2);
		$stop;
	end

endmodule

功能仿真

在这里插入图片描述

6.2边沿检测电路

在这里插入图片描述

设计代码edge_detect.v

module edge_detect(
	clk				,
	rst_n			,
	data			,
	raising_edge	,
	faling_edge		,
	double_edge		
);
	input		clk				;
	input		rst_n			;
	input		data			;
	output		raising_edge	;
	output		faling_edge		;
	output		double_edge		;
	
	reg data_reg0,data_reg1;

	always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		data_reg0 <= 1'b0;
		data_reg1 <= 1'b0;
	end
	else begin
		data_reg0 <= data;
		data_reg1 <= data_reg0;
	end
	
	assign raising_edge  = ~data_reg1 &&  data_reg0;
	assign faling_edge   =  data_reg1 && ~data_reg0;
	assign double_edge   =  data_reg1  ^  data_reg0;

endmodule

仿真文件edge_detect_tb.v

`timescale 1ns/1ns
`define clk_period 20

module edge_detect_tb;

	reg		clk				;
	reg		rst_n				;
	reg		data				;
	wire		raising_edge	;
	wire		faling_edge		;
	wire		double_edge		;

	edge_detect edge_detect_inst(
		.clk				(clk				),
		.rst_n			(rst_n			),
		.data				(data				),
		.raising_edge	(raising_edge	),
		.faling_edge	(faling_edge	),
		.double_edge	(double_edge	)	
	);

	initial clk = 1;
	always #(`clk_period/2) clk = ~clk;
	
	initial begin
		rst_n = 1'b0;
		#(`clk_period);
		rst_n = 1'b1;
	end
	
	initial begin
		data = 1'b0;
		#(`clk_period+10);
		data = 1'b1;
		#(`clk_period*3);
		data = 1'b0;
		#(`clk_period-5);
		data = 1'b1;
		#(`clk_period*2);
		data = 1'b0;
		#(`clk_period*3);
		$stop;
	end
endmodule

功能仿真

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐