安路SparkRoad串口转localbus实现指令控制
[复制链接]
Local Bus总线上的数据读写分为同步模式和异步模式。在同步模式下,需要一个外部时钟信号供接收端和发送端共用,利用时钟信号的上升沿对数据进行采样,SDRAM、SSRAM等高速信号使用同步模式;异步传输模式下,不使用时钟信号对数据进行采样(芯片内部还是需要有系统参考时钟来产生时序的),而是利用片选信号CS、写使能信号WE和读使能信号OE对数据进行采样,使用异步模式的器件有FLASH等。
我们使用串口转localbus是接口,实现各芯片的访问,扩展性强。
-
- output reg cpu_cs_n ,
- output reg cpu_wr_n ,
- output reg cpu_rd_n ,
- output reg [12:0] cpu_addr ,
- output reg [15:0] cpu_datain,
- input [15:0] cpu_dataout,
上面是我们常用的localbus接口,通过cs片选,选择不同的module,实现不同的功能
由于串口收发都是8位,采用接受四个字节,组成32bit的数据,高16位数据,低16位最高位读写标志位,接着就是2bit功能状态位,最后就是13bit的地址位
大致的串口转locabus协议就是这样,开始写代码
另外UART与locabus跨时钟的处理,采用握手
- `timescale 1ns / 1ps
-
- module uart_localbus(
- input clk ,
- input rst_n ,
-
- output reg [31:0] cpu_cs_n ,
- output reg cpu_wr_n ,
- output reg cpu_rd_n ,
- output reg [12:0] cpu_addr ,
- output reg [15:0] cpu_datain,
- input [15:0] cpu_dataout,
-
- input [7:0] uart_recv ,
- input uart_recv_vld ,
- output reg [7:0] uart_send ,
- output reg uart_send_vld ,
- input uart_ready
-
- );
-
-
-
-
- reg [2:0] recv_vld_dly ;
- reg [1:0] rx_cnt ;
- reg [1:0] tx_cnt ;
- reg [2:0] cpu_rd_dly ;
- reg [2:0] uart_ready_dly ;
- reg [15:0] send_reg ;
- reg [7:0] recv_data ;
- reg wr_flag ;
- reg [1:0] uart_code ;
- reg [12:0] uart_addr ;
- reg [15:0] uart_data ;
- reg cs_ctrl ;
- reg [1:0] recv_done_dly;
- reg tx_flag ;
-
-
- wire recv_vld_rising;
- wire recv_done ;
- wire cpu_rd_rising ;
- wire uart_ready_rising;
- wire recv_done_rising;
-
- assign recv_vld_rising = recv_vld_dly[2:1] == 2'b01 ? 1'b1 : 1'b0;
- assign recv_done = (rx_cnt == 2'b11)&&(recv_vld_rising == 1'b1) ? 1'b1 : 1'b0 ;
- assign cpu_rd_rising = cpu_rd_dly[2:1] == 2'b01 ? 1'b1 : 1'b0 ;
- assign uart_ready_rising = uart_ready_dly[1:0] == 2'b01 ? 1'b1 : 1'b0;
- assign recv_done_rising = recv_done_dly[1:0] == 2'b01 ? 1'b1 : 1'b0 ;
-
-
- always@(posedge clk)
- begin
- recv_vld_dly[2:0] <= {recv_vld_dly[1:0],uart_recv_vld};
- cpu_rd_dly[2:0] <= {cpu_rd_dly[1:0],cpu_rd_n} ;
- uart_ready_dly[1:0] <= {uart_ready_dly[1:0],uart_ready} ;
- recv_done_dly[1:0] <= {recv_done_dly[0],recv_done} ;
- end
-
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- recv_data <= 8'h00;
- else if(recv_vld_dly[0] == 1'b1)
- recv_data <= uart_recv ;
- else;
- end
-
- //recv data num
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- rx_cnt <= 2'b00;
- else if(recv_vld_rising)
- rx_cnt <= rx_cnt + 1'b1 ;
- end
-
-
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- begin
- wr_flag <= 1'b0 ;
- uart_code <= 2'b00 ;
- uart_addr <= 13'd0 ;
- uart_data <= 16'h00;
- end
- else if(recv_vld_rising)
- begin
- case(rx_cnt)
- 2'b00 : begin
- uart_addr[7:0] <= recv_data[7:0] ;
- end
- 2'b01 : begin
- wr_flag <= recv_data[7] ;
- uart_code <= recv_data[6:5] ;
- uart_addr[12:8] <= recv_data[4:0] ;
- end
- 2'b10 : begin
- uart_data[7:0] <= recv_data[7:0] ;
- end
- 2'b11 : begin
- uart_data[15:8] <= recv_data[7:0] ;
- end
- default: ;
- endcase
- end
- else ;
- end
-
- //cpu_cs_n
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- cs_ctrl <= 1'b1;
- else if(recv_done_rising)
- cs_ctrl <= 1'b0 ;
- else
- cs_ctrl <= 1'b1 ;
- end
-
- //cpu_wr_n
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- cpu_wr_n <= 1'b1;
- else if((recv_done_rising)&&(wr_flag == 1'b0))
- cpu_wr_n <= 1'b0 ;
- else
- cpu_wr_n <= 1'b1 ;
- end
-
-
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- cpu_rd_n <= 1'b1;
- else if(recv_done_rising&&(wr_flag == 1'b1))
- cpu_rd_n <= 1'b0 ;
- else
- cpu_rd_n <= 1'b1 ;
- end
-
- //返回的数据处理
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- send_reg <= 16'h0000;
- else if(cpu_rd_dly[0] == 1'b0)
- send_reg <= cpu_dataout ;
- end
-
- always@(posedge clk)
- begin
- cpu_addr <= uart_addr ;
- cpu_datain <= uart_data ;
-
- end
-
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- begin
- uart_send <= 8'h00;
- uart_send_vld <= 1'b0;
- tx_flag <= 1'b0 ;
- end
- else if((cpu_rd_rising == 1'b1)&&(uart_ready == 1'b1)&&(!tx_flag))
- begin
- uart_send[7:0] <= send_reg[7:0] ;
- uart_send_vld <= 1'b1 ;
- tx_flag <= 1'b1 ;
- end
- else if(tx_flag&&(uart_ready_rising == 1'b1))
- begin
- uart_send[7:0] <= send_reg[15:8] ;
- uart_send_vld <= 1'b1 ;
- tx_flag <= 1'b0 ;
- end
- else
- uart_send_vld <= 1'b0 ;
- end
-
-
-
-
- always@(*)
- begin
- case(cpu_addr[12:8])
- 5'd0 : cpu_cs_n = {31'hffffffff,cs_ctrl } ;
- 5'd1 : cpu_cs_n = {30'hffffffff,cs_ctrl, 1'h1} ;
- 5'd2 : cpu_cs_n = {29'hffffffff,cs_ctrl, 2'h3} ;
- 5'd3 : cpu_cs_n = {28'hffffffff,cs_ctrl, 3'h7} ;
- 5'd4 : cpu_cs_n = {27'hfffffff ,cs_ctrl, 4'hf} ;
- 5'd5 : cpu_cs_n = {26'hfffffff ,cs_ctrl, 5'h1f} ;
- 5'd6 : cpu_cs_n = {25'hfffffff ,cs_ctrl, 6'h3f} ;
- 5'd7 : cpu_cs_n = {24'hfffffff ,cs_ctrl, 7'h7f} ;
- 5'd8 : cpu_cs_n = {23'hffffff ,cs_ctrl, 8'hff} ;
- 5'd9 : cpu_cs_n = {22'hffffff ,cs_ctrl, 9'h1ff} ;
- 5'd10 : cpu_cs_n = {21'hffffff ,cs_ctrl,10'h3ff} ;
- 5'd11 : cpu_cs_n = {20'hffffff ,cs_ctrl,11'h7ff} ;
- 5'd12 : cpu_cs_n = {19'hfffff ,cs_ctrl,12'h1fff} ;
- 5'd13 : cpu_cs_n = {18'hfffff ,cs_ctrl,13'h3fff} ;
- 5'd14 : cpu_cs_n = {17'hfffff ,cs_ctrl,14'h7fff} ;
- 5'd15 : cpu_cs_n = {16'hfffff ,cs_ctrl,15'hffff} ;
- 5'd16 : cpu_cs_n = {15'hffff ,cs_ctrl,16'h1ffff} ;
- 5'd17 : cpu_cs_n = {14'hffff ,cs_ctrl,17'h3ffff} ;
- 5'd18 : cpu_cs_n = {13'hffff ,cs_ctrl,18'h7ffff} ;
- 5'd19 : cpu_cs_n = {12'hffff ,cs_ctrl,19'hfffff} ;
- 5'd20 : cpu_cs_n = {11'hfff ,cs_ctrl,20'h1fffff} ;
- 5'd21 : cpu_cs_n = {10'hfff ,cs_ctrl,21'h3fffff} ;
- 5'd22 : cpu_cs_n = { 9'hfff ,cs_ctrl,22'h7fffff} ;
- 5'd23 : cpu_cs_n = { 8'hfff ,cs_ctrl,23'hffffff} ;
- 5'd24 : cpu_cs_n = { 7'hff ,cs_ctrl,24'h1ffffff} ;
- 5'd25 : cpu_cs_n = { 6'hff ,cs_ctrl,25'h3ffffff} ;
- 5'd26 : cpu_cs_n = { 5'hff ,cs_ctrl,26'h7ffffff} ;
- 5'd27 : cpu_cs_n = { 4'hff ,cs_ctrl,27'hfffffff} ;
- 5'd28 : cpu_cs_n = { 3'hf ,cs_ctrl,28'h1fffffff} ;
- 5'd29 : cpu_cs_n = { 2'hf ,cs_ctrl,29'h3fffffff} ;
- 5'd30 : cpu_cs_n = { 1'hf ,cs_ctrl,30'h7fffffff} ;
- 5'd31 : cpu_cs_n = { cs_ctrl,31'hffffffff} ;
- default : ;
- endcase
- end
-
- endmodule
激励代码如下
- module uart_localbus_tb();
-
- `define PERIOD 10
-
- reg clk ;
- reg rst_n ;
- //localbus
- wire [31:0] cpu_cs_n ;
- wire cpu_wr_n ;
- wire cpu_rd_n ;
- wire [12:0] cpu_addr ;
- wire [15:0] cpu_datain ;
- reg [15:0] cpu_dataout;
- //UART STREAM
- reg [7:0] uart_recv ;
- reg uart_recv_vld ;
- wire [7:0] uart_send ;
- wire uart_send_vld ;
- reg uart_ready ;
- wire cs ;
- assign cs =((&cpu_cs_n) == 1'b0);
-
- uart_localbus u_uart_localbus(
- .clk ( clk ),
- .rst_n ( rst_n ),
-
- .cpu_cs_n ( cpu_cs_n ),
- .cpu_wr_n ( cpu_wr_n ),
- .cpu_rd_n ( cpu_rd_n ),
- .cpu_addr ( cpu_addr ),
- .cpu_datain ( cpu_datain ),
- .cpu_dataout ( cpu_dataout ),
-
- .uart_recv ( uart_recv ),
- .uart_recv_vld ( uart_recv_vld ),
- .uart_send ( uart_send ),
- .uart_send_vld ( uart_send_vld ),
- .uart_ready ( uart_ready )
- );
-
- loc_led u_loc_led(
- .clk (CLK_24M ),
- .rst (~rst_n ),
-
- .cpu_cs_n ( cpu_cs_n[0] ),
- .cpu_wr_n ( cpu_wr_n ),
- .cpu_rd_n ( cpu_rd_n ),
- .cpu_addr ( cpu_addr[7:0] ),
- .cpu_datain ( cpu_datain ),
- .cpu_dataout ( led_dataout ),
-
- .led (led )
- );
-
- initial
- begin
- clk = 1'b0 ;
- rst_n = 1'b0 ;
- cpu_dataout = 16'h1234;
- uart_recv = 8'b0;
- uart_recv_vld = 1'b0;
- uart_ready = 1'b1;
- $monitor($time,,"cpu_cs_n = %h,cpu_wr_n = %h,cpu_rd_n = %b,cpu_addr = %h,cpu_datain = %h",cpu_cs_n,cpu_wr_n,cpu_rd_n,cpu_addr,cpu_datain);
- #100
- rst_n = 1'b1;
- repeat(20)@(posedge clk);
- uart_send_data(32'hd5a50401);
- repeat(20)@(posedge clk);
- uart_send_data(32'h12340300);
- repeat(20)@(posedge clk);
- uart_send_data(32'h56788114);
- end
-
- always #(`PERIOD) clk <= ~clk ;
-
- reg [3:0] ready_cnt ;
-
- always@(posedge clk)
- ready_cnt <= uart_ready == 1'b0 ? ready_cnt + 1'b1 : 1'b0 ;
-
-
- always@(posedge clk)
- begin
- if(cs&&(!cpu_rd_n))
- begin
- uart_ready <= 1'b0 ;
- end
- else if(ready_cnt == 4'd10)
- begin
- uart_ready <= 1'b1 ;
- end
- end
-
-
- task uart_send_data;
- input [31:0] dat;
- begin
- #200 uart_recv = dat[7:0];
- #200 uart_recv_vld = 1'b1;
-
-
-
-
-
-
-
-
-
-
- end
- endtask
-
-
-
- endmodule
-
功能仿真的波形就不放了,感兴趣的自己可以跑。
编译把代码烧录到单板,
下面看下jtag仿真抓取得波形
这是通过串口下发4字节数据给FPGA.
通过抓取的波形读写功能都基本功能实现了,UART读写控制LED功能正常。
串口转localbus可以继续优化下,后续功能扩展位VGA显示屏。
|