IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> [FPGA系列] “乒乓操作”实战总结 -> 正文阅读

[嵌入式][FPGA系列] “乒乓操作”实战总结

一、基本概念

????????乒乓操作是FPGA开发中的一种数据缓冲优化设计技术,可以看成是另一种形式的流水线技术,具有节约缓冲空间、对数据流无缝处理等优点,其操作原理如图所示。

? ? ? ? 在输入数据流到达时,输入数据流选择单元对其流向进行控制,其执行流程:

在第一个缓冲周期,输入数据流写入数据缓冲模块1,写完之后进入第二个缓冲周期。

在第二个缓冲周期,输入数据流写入数据缓冲模块2,同时将数据缓冲模块1中的数据读出。

在第三个缓冲周期,输入数据流写入数据缓冲模块1,同时将数据缓冲模块2中的数据读出。

????????如此反复循环地操作,即为乒乓操作。

? ? ? ? 乒乓操作特点:实现跨时钟域的数据传输,其基本原理:

????????输入数据流的 面积 × 速度 = 输出数据流的 面积 × 速度

? ? ? ? 面积:即数据传输线的位宽,bit。

? ? ? ? 速度:即数据传输的时钟频率,hz。

????????例:输入数据流为 50Mhz × 8bit,则输出数据流为 25Mhz × 16bit,这样就实现了跨时钟域的数据传输。

?二、题目要求

? ? ? ? 使用数据产生模块输出 50Mhz × 8bit 的数据 8'b0 ~ 8'b199,通过乒乓操作读取数据,对其缓存并输出为 25Mhz × 16bit 的数据 16'h0100、16'h0302 ... 16'h6362 ... 16'hc7c6、16'h0100...

三、思路整理

? ? ? ? 将其划分为四个模块,如图所示。

? ? ? ? 模块框图如图所示。?

????????执行流程:

data_gen输出数据 0 ~ 199(50Mhz × 8bit)?。

在第一个缓冲周期,ram_ctrl 将数据流接入 ram1,ram1存入 0 ~ 99。

在第二个缓冲周期,ram_ctrl 将数据流接入 ram2,ram2存入 100 ~ 199,同时 ram_ctrl 将ram1中的数据输出,即16'h0100、16'h0302 ... 16'h6362(10'd99 10'd98)。

在第三个缓冲周期,ram_ctrl 将数据流接入 ram1,ram1存入 0 ~ 99,同时 ram_ctrl 将ram2中的数据输出,即16'h6463、16'h6665 ... 16'hc7c6(10'd199 10'd198)。

? ? ? ? 用状态机来实现这个功能。

四、Verilog实战

?1、data_gen模块

module data_gen
(
	input wire          clk		,	//50MHZ
	
	input wire 		    rst		,
	
	output wire         data_en	,
	
	output reg  [7:0]   data		
);



always@(posedge clk or negedge rst)
	if(!rst)
		data <= 8'b0;
	else if(data == 'd199)
		data <= 8'b0;
	else
		data <= data + 1'b1;
		
assign data_en = (rst == 1'b1);

endmodule

?2、ram_ctrl模块

module ram_ctrl
(
	input wire 				clk_25m			,
	input wire 				clk_50m			,
	input wire 				rst				,
	input wire 	[15:0] 	    ram1_data		,
	input wire 	[15:0] 	    ram2_data		,
	input wire 				data_en			,
	input wire 	[7:0]		data_in			,
	
	output wire 			ram1_wr_en		,
	output reg  [6:0]  	    ram1_wr_addr	,
	output wire	[7:0]  	    ram1_wr_data	,
	output wire 			ram1_rd_en		,
	output reg  [5:0]  	    ram1_rd_addr	,
		
	output wire 			ram2_wr_en		,
	output reg  [6:0] 	    ram2_wr_addr	,
	output wire [7:0] 	    ram2_wr_data	,
	output wire 			ram2_rd_en		,
	output reg  [5:0]  	    ram2_rd_addr	,
	
	output reg 	[7:0] 	    data_in_reg		,
	
	output wire [15:0] 	    data_out
);

parameter	IDLE 	= 4'b0001,
			WRAM1 = 4'b0010,
			R1_W2 = 4'b0100,
			W1_R2 = 4'b1000;
				
reg [3:0] state,next_state;

//data_in_reg:读取数据并打拍
always@(posedge clk_50m or negedge rst)
	if(!rst)
		data_in_reg <= 8'b0;
	else if(data_en == 1'b1)
		data_in_reg <= data_in;

//state:现态转移
always@(posedge clk_50m or negedge rst)
	if(!rst)
		state <= IDLE;
	else
		state <= next_state;
		
//next_state:次态改变
always@(*)
	case(state)
		IDLE  : next_state = WRAM1;
		WRAM1 : next_state = (data_in_reg == 'd99 )?R1_W2:WRAM1;
		R1_W2 : next_state = (data_in_reg == 'd199)?W1_R2:R1_W2;
		W1_R2 : next_state = (data_in_reg == 'd99 )?R1_W2:W1_R2;
		default : next_state = IDLE;
	endcase

//ram1、ram2读写使能
assign ram1_wr_en = (state == WRAM1 || state == W1_R2);
assign ram1_rd_en = (next_state == R1_W2 || state == R1_W2);
assign ram2_wr_en = (state == R1_W2);
assign ram2_rd_en = (next_state == W1_R2 || state == W1_R2);

//ram1_wr_addr,ram2_wr_addr:写地址计数
always@(posedge clk_50m or negedge rst)
	if(!rst)
		begin
			ram1_wr_addr <= 7'b0;
			ram2_wr_addr <= 7'b0;
		end
	else if(ram1_wr_addr == 'd99 || ram2_wr_addr == 'd99)
		begin
			ram1_wr_addr <= 7'b0;
			ram2_wr_addr <= 7'b0;
		end
	else
		case(state)
			WRAM1 : ram1_wr_addr <= ram1_wr_addr + 1'b1;
			R1_W2 : ram2_wr_addr <= ram2_wr_addr + 1'b1;
			W1_R2 : ram1_wr_addr <= ram1_wr_addr + 1'b1;	
			default :
				begin
					ram1_wr_addr <= 7'b0;
					ram2_wr_addr <= 7'b0;
				end			
		endcase
//ram1_rd_addr,ram2_rd_addr:读地址计数
always@(posedge clk_25m or negedge rst)
	if(!rst)
		begin
			ram1_rd_addr <= 6'b0;
			ram2_rd_addr <= 6'b0;
		end
	else if(ram1_rd_addr == 'd49 || ram2_rd_addr == 'd49)
		begin
			ram1_rd_addr <= 6'b0;
			ram2_rd_addr <= 6'b0;
		end
	else
		case(state)
			R1_W2 : ram1_rd_addr <= ram1_rd_addr + 1'b1;
			W1_R2 : ram2_rd_addr <= ram2_rd_addr + 1'b1;	
			default :
				begin
					ram1_rd_addr <= 6'b0;
					ram2_rd_addr <= 6'b0;
				end			
		endcase

//ram1、ram2输入数据选择		
assign ram1_wr_data = (state == WRAM1 || state == W1_R2) ? data_in_reg:8'b0;
assign ram2_wr_data = (state == R1_W2) ? data_in_reg:8'b0;

//ram1、ram2输出数据选择
assign data_out = (state == R1_W2) ? ram1_data:((state == W1_R2) ? ram2_data:16'b0);

		
endmodule

3、pingpang模块

module pingpang
(
	input 	wire 			clk		,
	input 	wire 			rst		,
	
	output	wire [15:0]     data_out
);

wire 				clk_25m			;
wire 				clk_50m			;
wire				locked			;
wire				rst_n			;

wire 	[15:0] 	    ram1_data		;
wire 	[15:0] 	    ram2_data		;
wire 				data_en			;
wire 	[7:0]		data_in			;
	
wire 				ram1_wr_en		;
wire 	[6:0]  	    ram1_wr_addr	;
wire 	[7:0]  	    ram1_wr_data	;
wire 				ram1_rd_en		;
wire 	[5:0]  	    ram1_rd_addr	;
	
wire 				ram2_wr_en		;
wire 	[6:0]  	    ram2_wr_addr	;
wire 	[7:0]  	    ram2_wr_data	;
wire 				ram2_rd_en		;
wire 	[5:0]  	    ram2_rd_addr	;
	
wire 	[7:0]  	    data_in_reg		;

assign rst_n = locked & rst;

clk_gen clk_gen_inst
(
	.areset	(~rst		),
	.inclk0	(clk		),
	.c0		(clk_50m	),
	.c1		(clk_25m	),
	.locked	(locked	    )
);	
data_gen data_gen_inst
(
	.clk		(clk_50m		),	//50MHZ	
	.rst		(rst_n		    ),
		
	.data_en	(data_en		),
	.data		(data_in		)		
);
ram_ctrl ram_ctrl_inst
(

	.clk_25m		(clk_25m		),
	.clk_50m		(clk_50m		),
	.rst			(rst_n			),
	.ram1_data		(ram1_data		),
	.ram2_data		(ram2_data		),
	.data_en	    (data_en		),
	.data_in		(data_in		),
		
	.ram1_wr_en		(ram1_wr_en		),
	.ram1_wr_addr	(ram1_wr_addr	),
	.ram1_wr_data	(ram1_wr_data	),
	.ram1_rd_en		(ram1_rd_en		),
	.ram1_rd_addr	(ram1_rd_addr	),
		
	.ram2_wr_en		(ram2_wr_en		),
	.ram2_wr_addr	(ram2_wr_addr	),
	.ram2_wr_data	(ram2_wr_data	),
	.ram2_rd_en		(ram2_rd_en		),
	.ram2_rd_addr	(ram2_rd_addr	),
		
	.data_in_reg	(data_in_reg	),
		
	.data_out		(data_out		)
);
ram ram1_inst
(
	.data			(ram1_wr_data	),
	.rdaddress	    (ram1_rd_addr	),
	.rdclock		(~clk_25m		),
	.rden			(ram1_rd_en		),
	.wraddress	    (ram1_wr_addr	),
	.wrclock		(~clk_50m		),
	.wren			(ram1_wr_en		),
	.q				(ram1_data		)
);
ram ram2_inst
(
	.data			(ram2_wr_data	),
	.rdaddress	    (ram2_rd_addr	),
	.rdclock		(~clk_25m		),
	.rden			(ram2_rd_en		),
	.wraddress	    (ram2_wr_addr	),
	.wrclock		(~clk_50m		),
	.wren			(ram2_wr_en		),
	.q				(ram2_data		)
);

endmodule

五、Modelsim仿真

????????可以看到数据流实现了无缝衔接,达到预期的效果,实验成功。

?

参考资料:野火《FPGA Verilog开发实战指南——基于Altera EP4CE10》

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-09 11:56:09  更:2021-09-09 11:57:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:23:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码