FPGA入门嵌入式 块RAM双口RAM使用

嵌入式 块RAM双口RAM使用

对RAM(随机存储器)进行读和写操作。
使用tool工具生成IP核RAM,8位读地址8位写地址。

IP核仿真
`timescale 1ns/1ns
`define clk_period 20
module dpram_tb;
   reg clock;
	reg [7:0]data;
	reg [7:0]rdaddress; 
   reg [7:0]wraddress;
   reg wren;
	wire [7:0]q;
	integer n;  //有符号数 寄存器类型
dpram dpram0(
	.clock(clock),
	.data(data),
	.rdaddress(rdaddress),  //读地址
	.wraddress(wraddress),  //写地址   写地址变化后慢一个时钟写入
	.wren(wren),  //写请求信号,高电平有效
	.q(q)  //读出来的结果 rdaddress地址后两个时钟出结果
	);

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

initial  begin
 data = 0;
 rdaddress = 20;
 wraddress = 0;
 wren = 0;
 #(`clk_period*20+1);
 for( n=0;n<=15; n=n+1)
  begin  
   wren = 1;
	data = 255-n;
	wraddress = n;
  #`clk_period;
  end
  
  wren = 0;
 #(`clk_period*50);
  for( n=0;n<=15;n=n+1)
  begin  
	rdaddress = n;
  #`clk_period;
  end
  #(`clk_period*10);
 $stop;
 
end		
endmodule

FPGA入门嵌入式 块RAM双口RAM使用_第1张图片
FPGA入门嵌入式 块RAM双口RAM使用_第2张图片

当读地址为零时,两个周期后q信号输出数据。延迟两个周期。

简易系统

FPGA入门嵌入式 块RAM双口RAM使用_第3张图片

Key_shake模块

将按键异步信号消抖处理,产生Key_flag和Key_state信号,处理按键按下事件。

module Key_shake(Clk,Rst_n,Key_in,Key_flag,Key_data);
   input Clk;
	input Rst_n;
	input Key_in;
	output reg Key_flag; //当按键按下20ms消除抖动成功后产生一个脉冲flag信号,检测到产生flag信号后产生按键按下低电平。
	output reg Key_data;
	
   reg [3:0]state; 
   reg  [19:0]count; //寄存器计数	
	reg   count_start;//时能计时
	reg  count_full;//计满脉冲信号
	reg reg0,reg1;
	reg key_in_now0,key_in_now1;
	wire rise,fall;
	
	localparam 
	High_pulse      =   4'b0001, //高电平稳定状态
	Low_eliminate   = 4'b0010, //键下降沿稳定状态
	Low_pulse       = 4'b0100, //低电平稳定状态
	High_eliminate  = 4'b1000; //键上升沿稳定状态
	
		always @(posedge Clk or negedge Rst_n)    //对输入按键信号做同步处理,消除异步信号亚稳态的影响。
	 if(!Rst_n) begin
       key_in_now0 <= 1'b0;
		 key_in_now1 <=1'b0;
		 end
      else   begin 
		 key_in_now0 <= Key_in;
	    key_in_now1 <= key_in_now0; 	
	end
	
	
	always @(posedge Clk or negedge Rst_n)    //脉冲边沿检测   两个寄存器
	 if(!Rst_n) begin
       reg0 <= 1'b0;
		 reg1 <=1'b0;
		 end
      else   begin 
		 reg0 <= key_in_now1;
	    reg1 <= reg0; 	
	end
	
	assign fall = !reg0 & reg1;  //检测到下降沿
	assign rise = reg0 & !reg1;  //检测到上升沿  
	
	 always @(posedge Clk  or negedge Rst_n)    //20ms计数器
       if(!Rst_n)
          count <= 20'b0;            	
	      else  if(count_start)
			  count <= count + 20'b1;
             else  count <= 20'b0;		  	
				 
	 always @(posedge Clk  or negedge Rst_n)     //20ms计数器计数满标志脉冲信号
       if(!Rst_n)
          count_full <= 1'b0;            	
	      else  if(count == 99_9999)
			  count_full <= 1'b1;
             else  count_full <= 1'b0;	
				 
	always @(posedge Clk or negedge Rst_n)
	 if(!Rst_n) begin
	  Key_data <= 1'b1;
	  state <= High_pulse;
	  Key_flag <= 1'b0;
	  count_start <= 1'b0;
         end		
	  else  case(state) 
	          High_pulse : begin  Key_flag <= 0;  
				                     Key_data <= 1'b1;
				   if(fall) begin
					    state <= Low_eliminate;
						 count_start <= 1'b1;   //使能开始20ms计数
	              end
					  else   state <= High_pulse;
					      end
				 Low_eliminate :  
   				if(count_full) begin
					     state <= Low_pulse;
						  Key_flag <= 1'b1;
				        Key_data <= 1'b0;
						  count_start <= 1'b0;   //关闭使能20ms计数
						 end
					 else begin  
					     if(rise) begin
						 state <= High_pulse;
						 count_start <= 1'b0;   //关闭使能20ms计数
						               end
					      else 
							 state <= Low_eliminate;
						         end
              
              Low_pulse:  begin Key_flag <= 1'b0;
                 if(rise) begin
					    state <= High_eliminate;
						 count_start <= 1'b1; //开始计时
						 end
					  else  state <= Low_pulse;
				            end
			     High_eliminate : 
				     if(count_full) begin
					   Key_data <= 1'b1;
						Key_flag <= 1'b1;
						state <= High_pulse;
					  end
					   else begin
						  if(fall) begin
						   state <= Low_pulse;
							count_start <= 1'b0;
							   end else 
								 state <= High_eliminate;
						            end		
		
		default : begin 
		               state <= High_pulse; //默认状态
							count_start <= 1'b0;
							Key_data <= 1'b1;
					    	Key_flag <= 1'b0;
		     end
	  endcase 

endmodule 
uart_data_rx模块

接收外来信号并传输到dpram中。

module uart_data_rx(  //串口接收模块
  Clk,
  Rst_n,
  Rs232_rx,
  Bps_set,
  Data_out,
  Done_rx
);

input Clk;
input Rst_n;
input Rs232_rx;
input [2:0]Bps_set;
output reg [7:0]Data_out;
output reg Done_rx;

reg [8:0]bps_max;//计数值
reg  now_reg0,rs232_rx_now;  //同步寄存器  消除亚稳态
reg  data_reg0,data_reg1;  //数据寄存器 判断高低电平
wire  low;
reg en_data;
reg  [8:0]bps_con; //bps_clk时钟计数器
reg bps_clk;
reg [7:0]bps_count;
reg [2:0]Data[7:0];   //共有8个Data数据每一个Data数据有3位 例如110
reg [2:0]start_bite;
reg [2:0]stop_bite;

//查找表选择计数值
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n)
  bps_max <= 9'b0;
 else begin
   case(Bps_set)
	 0: bps_max <= 9'd324;
	 1: bps_max <= 9'd162;
	 2: bps_max <= 9'd80;
	 3: bps_max <= 9'd53;
	 4: bps_max <= 9'd26; 
    default : bps_max <= 9'd324;
   endcase
 end
   //消除亚稳态
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n) begin
  now_reg0 <= 0;  
  rs232_rx_now <= 0;
end
 else begin
    now_reg0 <=  Rs232_rx;
    rs232_rx_now <= now_reg0;
 end 
 
//判断低电平
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n) begin
  data_reg0 <= 0;
  data_reg1 <= 0;
 end 
   else  begin  
  	 data_reg0 <= rs232_rx_now;
    data_reg1 <= data_reg0;     	
end
 	 
assign low = !data_reg0 & data_reg1;
//使能开始计数
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n) 
  bps_con <= 9'b0;
  else  
   if(en_data) begin 
	  if(bps_con == bps_max)
	   bps_con <= 9'b0;
	  else bps_con <= bps_con + 1'b1;
	end else  
	  bps_con <= 9'b0;
//产生波特率时钟
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n)
  bps_clk <= 0;
   else if(bps_con ==9'b1)
    bps_clk <= 1'b1;
	  else bps_clk <= 1'b0;
//计数波特率时钟
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n)
  bps_count <= 8'b0;
  else if(Done_rx || ((bps_count == 8'd12) && (start_bite >2)))
   bps_count <= 8'b0;
	 else if(bps_clk)
	  bps_count <= bps_count + 1'b1;
   
//产生Done_rx计数满信号
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n)
  Done_rx <= 1'b0;
 else if(bps_count == 8'd159)
  Done_rx <= 1'b1;
 else Done_rx <= 1'b0;
 
 //将Data的值给Data_out输出查看
always @(posedge Clk or  negedge Rst_n)
  if(!Rst_n)
    Data_out <= 8'b0;
 else if(bps_count == 8'd159) begin
    Data_out[0] <=  Data[0][2];   //如果中间6位数值和加起来大于3(100.101.110)第3位数值将大于1,由此可以将第3位的值直接传给Data_out数据 
    Data_out[1] <=  Data[1][2]; 
    Data_out[2] <=  Data[2][2]; 
    Data_out[3] <=  Data[3][2]; 
	 Data_out[4] <=  Data[4][2]; 
	 Data_out[5] <=  Data[5][2];
    Data_out[6] <=  Data[6][2];     
	 Data_out[7] <=  Data[7][2]; 	 
 end
 
//将Data的中间6位数据累加
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n)begin
  start_bite <= 3'b0;
  Data[0] <= 3'b0;
  Data[1] <= 3'b0;
  Data[2] <= 3'b0;
  Data[3] <= 3'b0;
  Data[4] <= 3'b0;
  Data[5] <= 3'b0;
  Data[6] <= 3'b0;
  Data[7] <= 3'b0;
  stop_bite  <= 3'b0;
 end   else  if(bps_clk)
  begin   
         case(bps_count)
         0:begin
		   	start_bite <= 3'b0;
	      	Data[0] <= 3'b0;
			   Data[1] <= 3'b0;
				Data[2] <= 3'b0;
				Data[3] <= 3'b0;
				Data[4] <= 3'b0;
				Data[5] <= 3'b0;
				Data[6] <= 3'b0;
				Data[7] <= 3'b0;
				stop_bite  <= 3'b0;			
           end
	      6,7,8,9,10,11: start_bite <= start_bite + rs232_rx_now; 		 
			22,23,24,25,26,27: Data[0] <= Data[0] + rs232_rx_now; 
		   38,39,40,41,42,43: Data[1] <= Data[1] + rs232_rx_now;   
		   54,55,56,57,58,59: Data[2] <= Data[2] + rs232_rx_now;
			70,71,72,73,74,75: Data[3] <= Data[3] + rs232_rx_now;
         86,87,88,89,90,91: Data[4] <= Data[4] + rs232_rx_now;
         102,103,104,105,106,107: Data[5] <= Data[5] + rs232_rx_now;
			118,119,120,121,122,123: Data[6] <= Data[6] + rs232_rx_now;
         134,135,136,137,138,139: Data[7] <= Data[7] + rs232_rx_now;
			150,151,152,153,154,155: stop_bite <= stop_bite + rs232_rx_now; 
        default : begin
		      start_bite <= start_bite;
	      	Data[0] <= Data[0];
			   Data[1] <= Data[1];
				Data[2] <= Data[2];
				Data[3] <= Data[3];
				Data[4] <= Data[4];
				Data[5] <= Data[5];
				Data[6] <= Data[6];
				Data[7] <= Data[7];
				stop_bite  <= stop_bite;			
		  end 
  endcase
    end
 
//判断是否完成接收或无开始错误,产生使能信号
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n)
	 en_data <= 1'b0;
  else if(low)
    en_data <= 1'b1;	 
 else if(Done_rx ||((bps_count == 8'd12) && (start_bite >2)))
   en_data <= 1'b0;
  else en_data <= en_data;	 
  
endmodule 
dpram模块

使用quartus2软件工具生成存储器IP核。

UART_TX模块

读取dpram中的值,con_en使能为高电平输出dapram中的值。

module UART_TX(
    Clk,
	 Rst_n,
	 Bps_set,
	 Con_en,
	 Data_in,
	 Rs232_tx,
	 Done_tx,
	 Uart_state 
  );
    input Clk;
    input Rst_n;
    input [2:0]Bps_set;
	 input Con_en;
	 input [7:0]Data_in;
	 output reg Rs232_tx;
	 output reg Done_tx;
	 output reg Uart_state;
	 
	 reg [15:0]count; //计数器定值
	 reg [15:0]high_count; //计数值 
	 reg [7:0]Data_in_r;
	 reg bps_clk;
	 reg [3:0]bps_count;
	 
	localparam start_bite = 1'b0;
	localparam stop_bite = 1'b1;
	
//查找表输出计数值
 always @(posedge Clk or negedge Rst_n)
  if(!Rst_n) 
	count <= 16'd5207;
	else begin   
	 case(Bps_set)
	  0: count <= 16'd5207;
	  1: count <= 16'd2603;
	  2: count <= 16'd1301;
	  3: count <= 16'd865;
	  4: count <= 16'd433;
	default : count <= 16'd5207;
	 endcase
	end
	
//产生Uart_state使能信号
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n)
  Uart_state <= 1'b0;
  else  if(Con_en)
 Uart_state <= 1'b1;
  else if(bps_count == 4'd11)
  Uart_state <= 1'b0;
   else Uart_state <= Uart_state; 
	
  //计数满产生bps_clk信号  
 always @(posedge Clk or negedge Rst_n)
  if(!Rst_n)
    high_count <= 0;
	  else if(Uart_state) begin
	     if(high_count == count)
	         high_count <= 0;
		    else high_count <= high_count + 1'b1;
			 end
		else high_count <= 0;
 
always @(posedge Clk or negedge Rst_n)
   if(!Rst_n)
	 bps_clk <= 1'b0;
	  else if(high_count == 16'b1)
	   bps_clk <= 1'b1;
		 else bps_clk <= 1'b0;
		 
//计数bps_clk的高电平到11计数满为0	产生Done_tx高点平信号   
always @(posedge Clk or negedge Rst_n)
  if(!Rst_n)
   bps_count <= 4'b0;
	 else if(bps_count == 4'd11)
	  bps_count <= 4'b0;
	  else if(bps_clk )
	   bps_count <= bps_count + 4'b1; 
	    else bps_count <= bps_count;
		 
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
 Done_tx <= 1'b0;
 else  if(bps_count == 4'd11) 
  Done_tx <= 1;
   else Done_tx <= 0;
	
always @(posedge Clk or negedge Rst_n )
 if(!Rst_n)
  Data_in_r <= 0;
   else if(Con_en)
	 Data_in_r <= Data_in ;
	 else Data_in_r <= Data_in_r; 
	
//十选一多路选择器	
always @(posedge Clk or negedge Rst_n)
 if(!Rst_n)
  Rs232_tx <= 1'b1;
 else begin
   case(bps_count)
	 0: Rs232_tx <=  1'b1;
	 1: Rs232_tx <=  start_bite;
	 2: Rs232_tx <=  Data_in_r[0];
	 3: Rs232_tx <=  Data_in_r[1];
	 4: Rs232_tx <=  Data_in_r[2];
    5: Rs232_tx <=  Data_in_r[3];
	 6: Rs232_tx <=  Data_in_r[4];
	 7: Rs232_tx <=  Data_in_r[5];
	 8: Rs232_tx <=  Data_in_r[6];
	 9: Rs232_tx <=  Data_in_r[7];
	 10: Rs232_tx <=  stop_bite;
	 default : Rs232_tx <=  1'b1;
	 
	 endcase 
 end
endmodule 
control模块

控制其他模块的输入输出。

module  control(
 Key_state,
 Key_flag,
 Clk,
 Rst_n,
 Done_rx,
 Done_tx,
 
 Rdaddress,
 Wraddress,
 Wren,
 En_send
  );

  input Key_flag;
  input Key_state;
  input Clk, Rst_n;
  input Done_rx,Done_tx;
  
  output reg [7:0]Rdaddress;
  output reg [7:0]Wraddress;
  output Wren;
  output reg En_send;
  
  reg put_button;
  reg delay_t0,delay_t1;
  
  assign Wren = Done_rx;

  
 always @(posedge Clk or negedge Rst_n)  //写入地址
  if(!Rst_n)
  Wraddress <= 8'd0;
  else  if(Done_rx) 
  Wraddress <= Wraddress + 1'b1; 
  else  Wraddress <= Wraddress ;
  
 always @(posedge Clk or negedge Rst_n)  //乒乓操作
  if(!Rst_n) 
   put_button <= 1'b0;
  else  if(Key_flag && !Key_state)
   put_button <= ~ put_button;
	
 always @(posedge Clk or negedge Rst_n)  //读操作
  if(!Rst_n)
  Rdaddress <= 8'b0;
  else  if(put_button && Done_tx)
  Rdaddress <= Rdaddress + 1'b1;
  else  Rdaddress <= Rdaddress; 
	
	//将信号延迟两个周期	
 always @(posedge Clk or negedge Rst_n)
  if(!Rst_n) begin
   delay_t0 <= 1'b0;
	delay_t1 <= 1'b0;
	end
	else begin 
	delay_t0 <= (put_button && Done_tx);
	delay_t1 <= delay_t0;
	end
	
  //控制产生发送使能信号
  always @(posedge Clk or negedge Rst_n)
   if(!Rst_n)
   En_send <= 1'b0;
   else if(delay_t1)  
	En_send <= 1'b1;
	else if(Key_flag && !Key_state)
	En_send <= 1'b1;
	else En_send <= 1'b0;
endmodule 
 //控制产生发送使能信号
  always @(posedge Clk or negedge Rst_n)
   if(!Rst_n)
   En_send <= 1'b0;
   else if(delay_t1)  
	En_send <= 1'b1;
	else if(Key_flag && !Key_state)
	En_send <= 1'b1;
	else En_send <= 1'b0;

if(Key_flag && !Key_state)条件为第一次按下按键后的产生En_send使能信号发送地址为0的数据,if(delay_t1) 为按下按键后每当tx模块输出一次地址数据完成产生Done_tx信号自动输出下一个地址数据。

Uart_dapram顶层模块
module Uart_dapram(
 Clk,
 Rs232_rx,
 Rst_n,
 Key_in,
 Rs232_tx
  );
 input Clk;
 input Rs232_rx;
 input Rst_n;
 input Key_in;
 output  Rs232_tx;

 wire [7:0]Data_bite_rx,Data_bite_tx;
 wire Done_tx,Done_rx;
 wire Key_flag,Key_data;
 wire Con_en;
 wire [7:0]rdaddress,wraddress;
 wire wren;

 
 UART_TX UART_TX0(  //串口发送模块
    .Clk(Clk),
	 .Rst_n(Rst_n),
	 .Bps_set(3'b0),
	 .Con_en(Con_en),
	 .Data_in(Data_bite_tx),
	 
	 .Rs232_tx(Rs232_tx),
	 .Done_tx(Done_tx), //一次发送完成标志信号
	 .Uart_state()   //模块状态标志信号
  );

 uart_data_rx uart_data_rx0(  //串口接收模块
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Rs232_rx(Rs232_rx),
  .Bps_set(3'b0),
  
  .Data_out(Data_bite_rx),
  .Done_rx(Done_rx)
);



Key_shake Key_shake0(
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Key_in(Key_in),
  
  .Key_flag(Key_flag),
  .Key_data(Key_data)
  );
  
  dpram dpram0(
	.clock(Clk),
	.data(Data_bite_rx),
	.rdaddress(rdaddress),  //读地址
	.wraddress(wraddress),  //写地址  
	.wren(wren),  //写请求信号,高电平有效
	
	.q(Data_bite_tx)
	);
	
 control control0(
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Done_rx(Done_rx),
  .Key_state(Key_data),
  .Key_flag(Key_flag),
  .Done_tx(Done_tx),
  
  .Rdaddress(rdaddress),
  .Wraddress(wraddress),
  .Wren(wren),
  .En_send(Con_en)
  );
endmodule  
Uart_dapram_tb顶层仿真模块
`timescale 1ns/1ns
`define clk_period 20

module Uart_dapram_tb;

 reg Clk,Con_en;
 reg [7:0]Data_in;
 wire Rs232_rx;
 reg Rst_n;
 wire Key_in;
 reg press;
 wire Rs232_tx;
 wire [2:0]Bps_set;
 wire Done_tx;
 assign Bps_set = 3'd0;

Uart_dapram Uart_dapram1(
 .Clk(Clk),
 .Rs232_rx(Rs232_rx),
 .Rst_n(Rst_n),
 .Key_in(Key_in),
 
 .Rs232_tx(Rs232_tx)
  );

  //用tx模块产生数据激励信号
  UART_TX UART_TX0(
    .Clk(Clk),
	 .Rst_n(Rst_n),
	 .Bps_set(Bps_set),
	 .Con_en(Con_en),
	 .Data_in(Data_in),
	 
	 .Rs232_tx(Rs232_rx),
	 .Done_tx(Done_tx),
	 .Uart_state() 
  );
  //产生按键信号 /按键模型
  Key_model Key_model0(
  .Press(press),
  .Key(Key_in)
  ); 
 
 initial Clk = 1;
 always#(`clk_period/2) Clk = ~Clk;
 
 initial begin
  Rst_n = 1'b0;
  Data_in = 8'b0;
  press = 1'b0;
  Con_en = 1'b0;
  #(`clk_period*20+1);
  Rst_n = 1'b1;
 #(`clk_period*20+1);
 
  Con_en = 1'b1;
  Data_in = 8'hae;
  #(`clk_period);
  Con_en = 1'b0;
  @(posedge Done_tx)
 
  #(`clk_period*3000);
    Con_en = 1'b1;
	Data_in = 8'h5b;
   #(`clk_period);
   Con_en = 1'b0;	 
  @(posedge Done_tx)
 
	#(`clk_period*3000);
	Con_en = 1'b1;
	Data_in = 8'hef;
   #(`clk_period);
   Con_en = 1'b0;	 
  @(posedge Done_tx)
  
  #(`clk_period*3000);
   Con_en = 1'b1;
	Data_in = 8'haa;
   #(`clk_period);
   Con_en = 1'b0;	 
 @(posedge Done_tx)
 
  #(`clk_period*3000);
  
  press = 1;
  #(`clk_period*5);
  press = 0;
	$stop;
 end

endmodule 

Key_model模块为按键事件模型,模拟实际中产生的按键抖动信号。
FPGA入门嵌入式 块RAM双口RAM使用_第4张图片
当读地址rdaddress变化后,dpram寄存器输出Data_bite_tx信号。

FPGA入门嵌入式 块RAM双口RAM使用_第5张图片

你可能感兴趣的:(FPGA,fpga,verilog,嵌入式,单片机)