尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于单端口RAM实现硬件自主的数据读写搬运系统

基于单端口RAM实现硬件自主的数据读写搬运系统 基于单端口 RAM 实现硬件自主的数据读写搬运系统一、引言“本项目利用 FPGA 内部的 RAM IP 核结合状态机实现了一套硬件自主的数据读写搬运系统。虽然没有实现完整的 DMA 控制器如突发传输、传输计数等但其核心思想——由硬件状态机控制数据流动无需 CPU 干预——已经体现了 DMA 的基本原理。”单端口RAM的读写公用一个地址不能同时进行二、项目需求及设计思路项目需求利用单端口RAM实现数据的写入、读取以及修改利用 DMA 的思想设计思路既然单端口的 RAM 不能同时写入和读取数据那我就控制读写使能在不同时间去进行数据的写入和读出就行了。三、项目实现过程3.1 模块设计大致设计思路确定了之后来思考需要哪些模块来实现我的想法。围绕着单端口的 RAM 来思考本次用到的 RAM 为下图所示。RAM 的调用会在其他文章中详细说明现在需要思考的是 data 和 address 从哪里产生我就利用按键来给出数据和地址但是我现在的按键只有五个若是想要得到多个数据或是想要得到较大的数据怎么办呢。则可以利用按键来做两个加减器来产生地址和数据还需要一个按键来完成读和写的切换。最后还需要显示我产生的数据这里就使用数码管更直观的查看。后续可能会继续往 DMA 方向优化和升级所以本次设计还是加入了状态机。现在就有了大致的模块了需要一个 data 产生模块来生成数据和地址按键消抖模块状态机模块数据显示模块由于项目本身比较简单我就直接把显示模块融入到顶层中了当然更规范的模块化设计可以进行分开顶层模块3.2 代码块“复位后默认处于读模式DU此时数码管显示 RAM 地址 0 中的数据。第一次按下 en 键后切换到写模式XIE随后可进行地址/数据写入。”顶层模块为了方便看到自己选择的地址以及输入的数据使用了 LED 灯和动态数码管最后呈现的效果就是数码管前三位显示的是读取的数据后三位显示输入的数据LED 灯显示地址module top ( input wire clk , input wire rst ,//我就用开关来做 input wire [3:0] key_data ,//数据的手动写入,对应的地址对应的数据,未消抖 input wire en ,//未消抖切换按键 output reg [5:0] sel , output reg [7:0] seg , output reg [3:0] led ); parameter XIE 0, DU 1, CNT_MAX 999_999, TIME 49_999 ; reg [15:0] cnt_time ; //1ms计数器 wire [3:0] addr ,//0-9的十个地址由key的第三位输入 data_in ;//写入的数据 wire [3:0] data ;//数据的输出 wire state_en;//消抖后的状态切换按键 wire state ;//状态机输出的现态 reg rd_en ,//读写使能 wr_en ; wire [3:0] key_out ; //根据状态机来控制 always (posedge clk or negedge rst) if(rst 0) begin wr_en 0; rd_en 0; end else case (state) XIE:begin wr_en 1;rd_en 0;end//只写 DU :begin wr_en 0;rd_en 1;end//读地址 default: begin wr_en 0;rd_en 1;end endcase //显示读出的数据 //位选切换计数器 always (posedge clk or negedge rst) if(!rst) cnt_time 0; else if(cnt_time TIME) cnt_time 0; else cnt_time cnt_time 1; //位选 always (posedge clk or negedge rst) if(!rst) sel 6b111_000; else if(cnt_time TIME) sel ~sel; //段选 always (posedge clk or negedge rst) if(rst 0) seg 8b1100_0000; else if(sel 6b111_000) case (data) 0 : seg 8b1100_0000; 1 : seg 8b1111_1001; 2 : seg 8b1010_0100; 3 : seg 8b1011_0000; 4 : seg 8b1001_1001; 5 : seg 8b1001_0010; 6 : seg 8b1000_0010; 7 : seg 8b1111_1000; 8 : seg 8b1000_0000; 9 : seg 8b1001_0000; hA:seg 8b1000_1000; hB:seg 8b1000_0011; hC:seg 8b1100_0110; hD:seg 8b1010_0001; hE:seg 8b1000_0110; hF:seg 8b1000_1110; default:seg 8b1100_0000; endcase else if(sel 6b000_111) case (data_in) 0 : seg 8b1100_0000; 1 : seg 8b1111_1001; 2 : seg 8b1010_0100; 3 : seg 8b1011_0000; 4 : seg 8b1001_1001; 5 : seg 8b1001_0010; 6 : seg 8b1000_0010; 7 : seg 8b1111_1000; 8 : seg 8b1000_0000; 9 : seg 8b1001_0000; hA:seg 8b1000_1000; hB:seg 8b1000_0011; hC:seg 8b1100_0110; hD:seg 8b1010_0001; hE:seg 8b1000_0110; hF:seg 8b1000_1110; default:seg 8b1100_0000; endcase //地址显示模块 always (*) if(rst 0) led 0; else case (addr) 0 : led 4b0000; 1 : led 4b0001; 2 : led 4b0010; 3 : led 4b0011; 4 : led 4b0100; 5 : led 4b0101; 6 : led 4b0110; 7 : led 4b0111; 8 : led 4b1000; 9 : led 4b1001; default:led 4b0000; endcase data data_inst( .clk (clk), .rst (rst), .key_1 (key_out[1]), .key_2 (key_out[0]), .num_key (data_in) ); data #( .MAX (9) )addr_inst( .clk (clk), .rst (rst), .key_1 (key_out[3]), .key_2 (key_out[2]), .num_key (addr) ); key #( .CNT_MAX (CNT_MAX) )key_4_inst( .clk (clk ), .rst (rst ), .key (key_data[3]), .key_out (key_out[3] ) ); key #( .CNT_MAX (CNT_MAX) )key_3_inst( .clk (clk ), .rst (rst ), .key (key_data[2]), .key_out (key_out[2] ) ); key #( .CNT_MAX (CNT_MAX) )key_2_inst( .clk (clk ), .rst (rst ), .key (key_data[1]), .key_out (key_out[1] ) ); key #( .CNT_MAX (CNT_MAX) )key_1_inst( .clk (clk ), .rst (rst ), .key (key_data[0]), .key_out (key_out[0] ) ); key #( .CNT_MAX (CNT_MAX) )en_inst( .clk (clk ), .rst (rst ), .key (en ), .key_out (state_en) ); state state_inst( .clk (clk ), .rst (rst ), .state_en (state_en), .state (state ) ); ram_data ram_data_inst ( .address ( addr ), .clock ( clk ), .data ( data_in ), .rden ( rd_en ), .wren ( wr_en ), .q ( data ) ); endmodule按键消抖模块module key( input wire clk , input wire rst , input wire key , output wire key_out ); parameter CNT_MAX 999_999 ;//20ms时间参数 reg [19:0] cnt_num ;//20ms计数器 reg [1:0] key_in ;//打拍消除亚稳态 /*-----------同步打拍消除亚稳态---------------*/ always (posedge clk or negedge rst) if(rst 0) key_in 1; else key_in {key_in[0],key};//等价于先执行key_in[0]key然后执行key_in[1]key_in[0] //20ms计数器 always (posedge clk or negedge rst) if(rst 0) cnt_num 0; else if(key_in[1] 0) if(cnt_num CNT_MAX) cnt_num cnt_num; else cnt_num cnt_num 1; else if(key_in[1] 1) cnt_num 0; //输出 assign key_out (cnt_num CNT_MAX - 1) ? 1 : 0; endmodule状态机模块module state ( input wire clk , input wire rst , input wire state_en,//切换状态的触发条件也就是控制状态跳变的输入信号 output wire state //初始为写地址的输入、然后是写数据、再摁一下是读地址根据读的地址给我相应的数据 ); //再摁一次又切换到写地址的输入这就是开始改上一次写入的数据了 parameter XIE 0, DU 1; reg [1:0] c_state,n_state; //现态、次态 //状态机第一段 always (posedge clk or negedge rst) if(rst 0) c_state DU; else c_state n_state ; //第二段状态转移 always (*) case (c_state) XIE:n_state (state_en) ? DU : XIE; DU:n_state (state_en) ? XIE : DU; default: n_state XIE; endcase //第三段、输出 assign state c_state; endmodule数据产生模块 通过加减按键来产生0-MAX的数据module data ( input wire clk , input wire rst , input wire key_1 ,//加 input wire key_2 ,//减 output reg [3:0] num_key //数据 ); parameter MAX 15; always (posedge clk or negedge rst) if(!rst) num_key 0; else if(key_1) if(num_key MAX) num_key num_key 1; else num_key 0; else if(key_2) if(num_key 0) num_key num_key - 1; else num_key MAX; endmodule仿真代码timescale 1ns/1ns module tb ; reg clk ; reg rst ; reg [3:0] key_data ; reg en ; wire [5:0] sel ; wire [7:0] seg ; initial begin clk 0 ; rst 0 ; en 1 ;//初始未消抖但是现在的状态是读地址选中的是0 key_data 4b1111 ; #20 rst 1 ; #40 en 0 ;#500 //写数据模式 en 1 ;#500 key_data 4b1101 ;#500 //写入1 key_data 4b1111 ;#500 en 0 ;#500 //进入读模式 en 1 ;#500 //现在的数码管可以读出第一位其余都为0 en 0 ;#500 //写地址模式还停留在1 en 1 ;#500 key_data 4b0111 ;#500 //来到地址2 key_data 4b1111 ;#500 en 0 ;#500 //写数据模式 en 1 ;#500 key_data 4b1101 ;#500 //写入2 key_data 4b1111 ;#500 en 0 ;#500 //进入读模式 en 1 ;#500 //现在的数码管可以读出第二位 en 0 ;#500 en 1 ;#500 key_data 4b0111 ;#500 key_data 4b1111 ;#500 en 0 ;#500 en 1 ;#500 key_data 4b1101 ;#500 key_data 4b1111 ;#500 en 0 ;#500 en 1 ;#500 en 0 ;#500 en 1 ;#500 key_data 4b0111 ;#500 key_data 4b1111 ;#500 en 0 ;#500 en 1 ;#500 key_data 4b1101 ;#500 key_data 4b1111 ;#500 en 0 ;#500 en 1 ;#500 en 0 ;#500 en 1 ;#500 key_data 4b0111 ;#500 key_data 4b1111 ;#500 en 0 ;#500 en 1 ;#500 key_data 4b1101 ;#500 key_data 4b1111 ;#500 en 0 ;#500 en 1 ;#500 $stop ; end always #10 clk ~clk; top #( .CNT_MAX (9) )top_inst ( .clk (clk ), .rst (rst ), .key_data (key_data), .en (en ), .sel (sel ), .seg (seg ) ); endmodule3.3 仿真波形图
返回列表