安路SparkRoad串口转localbus实现指令控制
[复制链接]
Local Bus总线上的数据读写分为同步模式和异步模式。在同步模式下,需要一个外部时钟信号供接收端和发送端共用,利用时钟信号的上升沿对数据进行采样,SDRAM、SSRAM等高速信号使用同步模式;异步传输模式下,不使用时钟信号对数据进行采样(芯片内部还是需要有系统参考时钟来产生时序的),而是利用片选信号CS、写使能信号WE和读使能信号OE对数据进行采样,使用异步模式的器件有FLASH等。
我们使用串口转localbus是接口,实现各芯片的访问,扩展性强。
//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 ,
//localbus
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,
//UART STREAM
input [7:0] uart_recv ,
input uart_recv_vld ,
output reg [7:0] uart_send ,
output reg uart_send_vld ,
input uart_ready
);
//localparam
//reg define
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 define
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
//接收4个字节数据处理
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
//cpu_rd_n
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 ;
// uart_send_vld <= cpu_rd_rising == 1'b1 ? 1'b1 : 1'b0 ;
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;
#200 uart_recv_vld = 1'b0;
#1000 uart_recv = dat[15:8];
#100 uart_recv_vld = 1'b1;
#100 uart_recv_vld = 1'b0;
#1000 uart_recv = dat[23:16];
#100 uart_recv_vld = 1'b1;
#100 uart_recv_vld = 1'b0;
#1000 uart_recv = dat[31:24];
#100 uart_recv_vld = 1'b1;
#100 uart_recv_vld = 1'b0;
end
endtask
endmodule
功能仿真的波形就不放了,感兴趣的自己可以跑。
编译把代码烧录到单板,
下面看下jtag仿真抓取得波形
这是通过串口下发4字节数据给FPGA.
通过抓取的波形读写功能都基本功能实现了,UART读写控制LED功能正常。
串口转localbus可以继续优化下,后续功能扩展位VGA显示屏。
|