Xilinx-Verilog-学习笔记(15):Verilog基础语法演示(2)

Xilinx-Verilog-学习笔记(15):Verilog基础语法演示(2)

一、移位寄存器

移位寄存器原理:
按照时钟节拍,一位一位移进来,起初寄存器中的值为0,lvds发送过来的数据为lvds d1。本实验采取从左侧移入的方法。

第一个时钟周期:{lvds_d1 0000_000}
第二个时钟周期:{lvds_d2 lvds_d1 000_000}
······
第八个时钟周期:{lvds_d8 lvds_d7 ···· lvds_d1}

其中,花括号为位拼接符,新移进来的1位与之前移位寄存器中的前7位进行拼接。

1、design文件
//接口区
module ex_shift_reg(
	input	wire		lvds_clk,	//lvds的时钟
	input	wire		rst_n,		//复位
	input	wire		lvds_d,		//lvds输入的数据
	output	reg	[7:0]	o_lvds_d	//寄存器输入的数据
);

//变量定义区
reg [7:0]	shift_reg;	//移位寄存器,共8位
reg	[2:0]	s_cnt;		//计数器
reg			s_flag;		//标志位
reg			s_flag_dly1, s_flag_dly2;	//打两拍延迟

//一位一位从左侧移入
always @(posedge lvds_clk or negedge rst_n)
	if(rst_n == 1'b0)
		shift_reg <= 'd0;
	else
		shift_reg <= {lvds_d,shift_reg[7:1]};

//计数器记移8次
always @(posedge lvds_clk or negedge rst_n)
	if(rst_n == 1'b0)
		s_cnt <= 'd0;
	else
		s_cnt <= s_cnt + 1'b1;
		
//移位寄存器8位被占满		
always @(s_cnt)
	if(s_cnt == 3'd7)
		s_flag <= 1'b1;
	else
		s_flag <= 1'b0;
	
//将寄存器中的数,输出出去
always @(posedge lvds_clk or negedge rst_n)
	if(rst_n == 1'b0)
		o_lvds_d <= 'd0;
	else if(s_flag_dly2 == 1'b1)
		o_lvds_d <= shift_reg;

//打两拍
always @(posedge lvds_clk)
	{s_flag_dly2,s_flag_dly1} <= {s_flag_dly1, s_flag};
	
endmodule

如何写全敏感列表:
条件表里的所有变量,以及赋值语句右边的所有变量。

打两拍:
除s_flag寄存器外,又额外定义了两个寄存器s_flag_dly1和s_flag_dly2。s_flag的值先移到s_flag1中,再移到s_flag2中,共需要2个时钟周期。

2、testbench文件
`timescale 1ns/1ns

module tb_ex_shift_reg;

reg				lvds_clk;	//lvds的时钟
reg				rst_n;		//复位
reg				lvds_d;		//lvds输入的数据
wire	[7:0]	o_lvds_d;	//寄存器输入的数据
reg 	[0:0]	mem1x16	 [15:0];	//存储区变量,位宽为1,深度为16
reg		[3:0]	i_30;		//取低四位

always #10 lvds_clk <= ~lvds_clk;

initial begin
	lvds_clk = 0;
	rst_n = 0;
	lvds_d = 0;
	#100
	rst_n = 1;
	end

initial begin
	$readmemb("./data.txt",mem1x16);
	end
	
initial begin 
	#100
	lvds_send_d();
	end
	
ex_shift_reg ex_shift_reg_inst(
	.lvds_clk		(lvds_clk),		//lvds的时钟
	.rst_n			(rst_n),		//复位
	.lvds_d			(lvds_d),		//lvds输入的数据
	.o_lvds_d		(o_lvds_d)		//寄存器输入的数据
);

task lvds_send_d();
	integer i;
	begin
		for(i=0;i<255;i=i+1)
		begin
			@(posedge lvds_clk);
			lvds_d <= mem1x16[i[3:0]];
			i_30 <= i[3:0];
		end
	end
endtask
	
endmodule

mem变量的使用方法:
存储器:
有地址,有深度。
左边为存储器位宽,中间是变量名字, 右边是深度
reg [0:0] mem1x16 [15:0];

mem变量填充方式:
$readmemh $readmemb

h为十六进制填充,b为二进制填充。
$readmemb("./data.txt",mem1x16);

读取方式:
mem1x16[i[3:0]]
一次读4位。

3、仿真波形

在这里插入图片描述
解析:lvds_d为lvds接收进来的数据,该数据通过将外部数据读入到存储器mem1x16中,之后从存储器中一次读1位到lvds_d中,因此lvds_d的数据依次为1010_1010_1000_1000。在下一个时钟上升沿来临时,将lvds_d的值拼接到移位寄存器的最高位(见拼接示意)。移位寄存器计数器的作用是用来记是否移完8位,如果移完则移位标志位置1,表示移位寄存器中8位已满,可以输出。如图所示,当使用s_flag时,对应的shift_reg提前了2拍,所以需要推后2拍在输出。

拼接示意:
第一个时钟周期:{1 0000_000}
第二个时钟周期:{0 1000_000}
第三个时钟周期:{1 0100_000}
第四个时钟周期:{0 1010_000}
第五个时钟周期:{1 0101_000}
第六个时钟周期:{0 1010_100}
第七个时钟周期:{1 0101_010}
第八个时钟周期:{0 1010_101}
最终输出来为8’h01010101,即为8’h55

二、modelsim脚本仿真

1、Tcl语言

在这里插入图片描述

2、do文件

此run.do文件为上面移位寄存器的。

#退出当前仿真工程
quit -sim
#清除命令行显示信息
.main clear
#创建一个库(目录)
vlib lib
vlib ./lib/work
#映射逻辑库的名字到物理路径上
vmap work ./lib/work
#将所有东西都编译到work库里面
vlog -work work .tb_ex_shift_reg.v
vlog -work work ./../design/*.v
#优化
vsim -voptargs=+acc work.tb_ex_shift_reg
#添加波形
add wave -divider {tb_module}
add wave tb_ex_shift_reg/lvds_d
add wave tb_ex_shift_reg/o_lvds_d 
add wave tb_ex_shift_reg/i_30 
add wave tb_ex_shift_reg/lvds_clock
add wave tb_ex_shift_reg/rst_n
add wave -divider {module_inst}
add wave tb_ex_shift_reg/tb_ex_shift_reg_inst/*
#启动仿真
run 100us

三、mealy型状态机

这里以检测11101序列为例,状态迁移图如下图:
在这里插入图片描述

1、design文件
//检测11101序列
module mealy(
	input	wire	s_clk,
	input	wire	rst_n,
	input	wire	A,
	output	reg		k
);

//定义了6种状态
parameter	S1 = 6'b00_0001;
parameter	S2 = 6'b00_0010;
parameter	S3 = 6'b00_0100;
parameter	S4 = 6'b00_1000;
parameter	S5 = 6'b01_0000;
parameter	S6 = 6'b10_0000;

reg	[5:0]	Curr_st;

always @(posedge s_clk or negedge rst_n)
	if(rst_n == 0)
		Curr_st <= S1;
	else
		case(Curr_st)
			S1:if(A == 1'b1)
					Curr_st <= S2;
			   else
					Curr_st <= S1;
			S2:if(A == 1'b1)
					Curr_st <= S3;
			   else
					Curr_st <= S1;
			S3:if(A == 1'b1)
					Curr_st <= S4;
			   else
					Curr_st <= S1;
			S4:if(A == 1'b0)
					Curr_st <= S5;
			   else
					Curr_st <= S4;
			S5:if(A == 1'b1)
					Curr_st <= S6;
			   else 
					Curr_st <= S1;
			S6:Curr_st <= S1;
			default:Curr_st <= S1;
		endcase
		
always @(posedge s_clk or negedge rst_n)
	if(rst_n == 1'b0)
		k <= 1'b0;
	else if(Curr_st == S5 && A == 1'b1)
		k <= 1'b1;
	else
		k <= 1'b0;


endmodule
2、testbench文件
`timescale 1ns/1ns

module tb_mealy;
	reg			s_clk;
	reg			rst_n;
	reg			a_in;
	wire		k_out;	
	
initial begin
		s_clk = 0;
		rst_n = 0;
		#100
		rst_n = 1;
	end
	
initial begin
		#200;
		rand_bit();
	end
	
always #10 s_clk <= ~s_clk;

mealy mealy_inst(
	.s_clk		(s_clk),
	.rst_n		(rst_n),
	.A			(a_in),
	.k			(k_out)
);

task	rand_bit();
	integer i;
	begin
		for(i=0;i<255;i=i+1)
		begin
			@(posedge s_clk);
			a_in <= {$random} % 2;	//产生0,1随机数
		end
	end
endtask

endmodule

知识点:
$random %10:产生-10到9的随机数。

${random}%10:产生0到10的随机数。

3、do文件
quit	-sim
.main	clear

vlib	./lib/
vlib	./lib/work_a/
vlib	./lib/design/

vmap	base_space ./lib/work_a/
vmap	design	./lib/design/

vlog	-work	base_space	./tb_mealy.v
vlog	-work	design		./../design/*.v
#-t指运行仿真的时间精度是ns		
#-L是链接库关键字
vsim	-t ns -voptargs=+acc -L base_space -L design base_space.tb_mealy

#创建虚拟信号
virtual type {
{01 S1}
{02 S2}
{04 S3}
{08 S4}
{10 S5}
{20 S6}
} vir_new_signal

add		wave	-divider {tb_mealy_1}
add		wave	tb_mealy/*
add		wave	-divider {mealy}
#顶层/例化的名字/* 其中*是通配符
add		wave	tb_mealy/mealy_inst/*
#创建一个vir_new_signal 类型的信号,也就是把Curr_st进行类型转换
virtual function {(vir_new_signal)tb_mealy/mealy_inst/Curr_st} new_state
add wave -color blue tb_mealy/mealy_inst/new_state

run		1us

知识点:
(1)vmap将base_space映射到实际目录./lib/work_a/下,vlog在编译过程中将./tb_mealy.v编译后的结果放到了base_space下,即放到了目录./lib/work_a/下。
(2)-t ns表示以ns级进行仿真。
(3)-L base_space表示在仿真时将base_space链接进来。
(4)virtual用于创建虚拟信号,其中信号S1的独热码为6’b000001,即为01,通过S1表示它。 (vir_new_signal) 相当于一种强制类型转换。

4、仿真波形

在这里插入图片描述
解析:A信号随机产生0或1,从图中可以看出,new_state的状态变化为S1->S2->S3->S4->S5,这是与Curr_st对应的。当进入S5状态后,且A的值为1,那么下一个时钟沿到来后,进入S6状态。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DDS(Direct Digital Synthesis)直接数字合成技术是一种数字信号处理技术,用于产生高精度、高稳定度、高分辨率的周期性信号。DDS技术的主要思路是:将一个固定的参考频率信号和一个可变的相位调制信号相乘,从而产生所需频率的输出信号。 在MATLAB中,我们可以通过使用内置函数sin()来生成正弦波信号。例如,我们可以生成一个频率为10 Hz,振幅为1的正弦波信号,并将其绘制成图形: ``` t = 0:0.001:1; x = sin(2*pi*10*t); plot(t,x); ``` 在Verilog中,我们可以使用DDS模块来生成正弦波信号。以下是一个简单的DDS模块: ``` module dds( input clk, //时钟信号 input reset, //复位信号 output reg [7:0] sin_out //正弦波输出信号 ); reg [31:0] phase_acc; //相位累加器 reg [7:0] sin_lut [0:255]; //正弦波查找表 //初始化正弦波查找表 initial begin for (i = 0; i < 256; i = i + 1) begin sin_lut[i] = $signed(127*sin(2*3.14159*i/256)); end end always @(posedge clk) begin if (reset) begin phase_acc <= 0; sin_out <= 0; end else begin phase_acc <= phase_acc + 100; //相位累加器步进为100 sin_out <= sin_lut[phase_acc[31:24]]; //从查找表中读取正弦波值 end end endmodule ``` 在这个DDS模块中,我们使用相位累加器来控制正弦波的频率,使用查找表来存储正弦波的值。在时钟上升沿时,相位累加器步进100,从查找表中读取正弦波值,并将其输出。 需要注意的是,在这个DDS模块中,我们使用了固定的步进值100。如果我们想要生成不同频率的正弦波信号,我们需要改变步进值。例如,如果我们想要生成频率为1 kHz的正弦波信号,我们需要将步进值改为1000*256/时钟频率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值