冒险武者 发表于 2022-5-16 23:23

安路SparkRoad串口转localbus实现指令控制

<p>Local Bus总线上的数据读写分为同步模式和异步模式。在同步模式下,需要一个外部时钟信号供接收端和发送端共用,利用时钟信号的上升沿对数据进行采样,SDRAM、SSRAM等高速信号使用同步模式;异步传输模式下,不使用时钟信号对数据进行采样(芯片内部还是需要有系统参考时钟来产生时序的),而是利用片选信号CS、写使能信号WE和读使能信号OE对数据进行采样,使用异步模式的器件有FLASH等。</p>

<p>我们使用串口转localbus是接口,实现各芯片的访问,扩展性强。</p>

<pre>
<code>//localbus
outputreg         cpu_cs_n,
outputreg         cpu_wr_n,
outputreg         cpu_rd_n,
outputreg cpu_addr,
outputreg cpu_datain,
input       cpu_dataout,</code></pre>

<p>上面是我们常用的localbus接口,通过cs片选,选择不同的module,实现不同的功能</p>

<p>&nbsp;</p>

<p>由于串口收发都是8位,采用接受四个字节,组成32bit的数据,高16位数据,低16位最高位读写标志位,接着就是2bit功能状态位,最后就是13bit的地址位</p>

<p>大致的串口转locabus协议就是这样,开始写代码</p>

<p>另外UART与locabus跨时钟的处理,采用握手</p>

<pre>
<code>`timescale 1ns / 1ps

module uart_localbus(
input          clk ,
input          rst_n   ,
//localbus
outputreg cpu_cs_n,
outputreg         cpu_wr_n,
outputreg         cpu_rd_n,
outputreg cpu_addr,
outputreg cpu_datain,
input       cpu_dataout,
//UART STREAM
input      uart_recv,
input               uart_recv_vld ,
outputreguart_send,
outputreg         uart_send_vld ,
input               uart_ready

);

//localparam

//reg define
reg   recv_vld_dly ;
reg   rx_cnt ;
reg   tx_cnt ;
reg   cpu_rd_dly   ;
reg   uart_ready_dly ;
reg    send_reg   ;
reg   recv_data   ;
reg            wr_flag      ;
reg   uart_code    ;
reg    uart_addr    ;
reg    uart_data    ;
reg            cs_ctrl      ;
reg   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'b01 ? 1'b1 : 1'b0;
assign    recv_done       =(rx_cnt == 2'b11)&amp;&amp;(recv_vld_rising == 1'b1) ? 1'b1 : 1'b0 ;
assign    cpu_rd_rising   =cpu_rd_dly == 2'b01 ? 1'b1 : 1'b0;
assign    uart_ready_rising =uart_ready_dly == 2'b01 ? 1'b1 : 1'b0;
assign    recv_done_rising = recv_done_dly == 2'b01 ? 1'b1 : 1'b0 ;

//接收同步打拍
always@(posedge clk)
begin
   recv_vld_dly&lt;={recv_vld_dly,uart_recv_vld};
   cpu_rd_dly    &lt;={cpu_rd_dly,cpu_rd_n}   ;
   uart_ready_dly&lt;={uart_ready_dly,uart_ready} ;
   recv_done_dly &lt;={recv_done_dly,recv_done} ;
end

always@(posedge clk or negedge rst_n)
begin
   if(rst_n == 1'b0)
      recv_data&lt;= 8'h00;
   else if(recv_vld_dly == 1'b1)
      recv_data&lt;= uart_recv ;
   else;
end

//recv data num
always@(posedge clk or negedge rst_n)
begin
   if(rst_n == 1'b0)
      rx_cnt&lt;=2'b00;
   else if(recv_vld_rising)
      rx_cnt&lt;=rx_cnt + 1'b1 ;
end

//接收4个字节数据处理
always@(posedge clk or negedge rst_n)
begin
   if(rst_n == 1'b0)
   begin
      wr_flag   &lt;=1'b0;
      uart_code &lt;=2'b00 ;
      uart_addr &lt;=13'd0 ;
      uart_data &lt;=16'h00;
   end
   else if(recv_vld_rising)
   begin
      case(rx_cnt)
      2'b00 : begin
            uart_addr &lt;=recv_data ;         
      end
      2'b01 : begin
              wr_flag   &lt;=recv_data   ;
            uart_code &lt;=recv_data ;
            uart_addr &lt;=recv_data ;         
      end
      2'b10 : begin
            uart_data &lt;=recv_data ;         
      end
      2'b11 : begin
            uart_data &lt;=recv_data ;         
      end
      default: ;
      endcase
   end
   else ;
end

//cpu_cs_n
always@(posedge clk or negedge rst_n)
begin
   if(rst_n == 1'b0)
      cs_ctrl&lt;=1'b1;
   else if(recv_done_rising)
      cs_ctrl&lt;=1'b0 ;
   else
      cs_ctrl&lt;=1'b1 ;
end

//cpu_wr_n
always@(posedge clk or negedge rst_n)
begin
   if(rst_n == 1'b0)
      cpu_wr_n&lt;=1'b1;
   else if((recv_done_rising)&amp;&amp;(wr_flag == 1'b0))
      cpu_wr_n&lt;=1'b0 ;
   else
      cpu_wr_n&lt;=1'b1 ;
end

//cpu_rd_n
always@(posedge clk or negedge rst_n)
begin
   if(rst_n == 1'b0)
      cpu_rd_n&lt;=1'b1;
   else if(recv_done_rising&amp;&amp;(wr_flag == 1'b1))
      cpu_rd_n&lt;=1'b0 ;
   else
      cpu_rd_n&lt;=1'b1 ;
end

//返回的数据处理
always@(posedge clk or negedge rst_n)
begin
   if(rst_n == 1'b0)
      send_reg&lt;=16'h0000;
   else if(cpu_rd_dly == 1'b0)
      send_reg&lt;=cpu_dataout ;
end

always@(posedge clk)
begin
   cpu_addr   &lt;=uart_addr ;
   cpu_datain &lt;=uart_data ;
//   uart_send_vld&lt;= 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&lt;=8'h00;
      uart_send_vld &lt;= 1'b0;
      tx_flag &lt;= 1'b0 ;
   end
   else if((cpu_rd_rising == 1'b1)&amp;&amp;(uart_ready == 1'b1)&amp;&amp;(!tx_flag))
   begin
      uart_send&lt;=send_reg ;
      uart_send_vld&lt;= 1'b1 ;
      tx_flag &lt;= 1'b1 ;
   end
   else if(tx_flag&amp;&amp;(uart_ready_rising == 1'b1))
   begin
      uart_send&lt;=send_reg ;
      uart_send_vld &lt;= 1'b1 ;
      tx_flag &lt;= 1'b0 ;
   end
   else
      uart_send_vld&lt;=1'b0 ;
end




always@(*)
begin
   case(cpu_addr)
      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</code></pre>

<p>激励代码如下</p>

<pre>
<code>module uart_localbus_tb();

`definePERIOD10

reg          clk       ;
reg          rst_n   ;
//localbus
wire    cpu_cs_n   ;
wire         cpu_wr_n   ;
wire         cpu_rd_n   ;
wire cpu_addr   ;
wire cpu_datain ;
reg    cpu_dataout;
//UART STREAM
reg   uart_recv   ;
reg          uart_recv_vld ;
wireuart_send   ;
wire         uart_send_vld ;
reg          uart_ready      ;
wirecs ;
       assign cs =((&amp;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   ),
.cpu_wr_n      ( cpu_wr_n      ),
.cpu_rd_n      ( cpu_rd_n      ),
.cpu_addr      ( cpu_addr ),
.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 &lt;= ~clk ;

reg ready_cnt ;

always@(posedge clk)
   ready_cnt &lt;= uart_ready == 1'b0 ? ready_cnt + 1'b1 : 1'b0 ;


always@(posedge clk)
begin
   if(cs&amp;&amp;(!cpu_rd_n))   
   begin
      uart_ready &lt;= 1'b0 ;
   end
   else if(ready_cnt == 4'd10)
   begin
      uart_ready &lt;= 1'b1 ;
   end
end
   
   
task uart_send_data;
   input   dat;
begin
#200uart_recv   = dat;
#200uart_recv_vld = 1'b1;
#200uart_recv_vld = 1'b0;
#1000 uart_recv   = dat;
#100uart_recv_vld = 1'b1;
#100uart_recv_vld = 1'b0;
#1000 uart_recv   = dat;
#100uart_recv_vld = 1'b1;
#100uart_recv_vld = 1'b0;
#1000 uart_recv   = dat;
#100uart_recv_vld = 1'b1;
#100uart_recv_vld = 1'b0;   
end   
endtask



endmodule
</code></pre>

<p>&nbsp;</p>

<p>功能仿真的波形就不放了,感兴趣的自己可以跑。</p>

<p>编译把代码烧录到单板,</p>

<p>下面看下jtag仿真抓取得波形</p>

<p></p>

<p>这是通过串口下发4字节数据给FPGA.</p>

<p>&nbsp;</p>

<p></p>

<p></p>

<p>通过抓取的波形读写功能都基本功能实现了,UART读写控制LED功能正常。</p>

<p></p>

<p>串口转localbus可以继续优化下,后续功能扩展位VGA显示屏。</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

lugl4313820 发表于 2022-5-21 20:04

加油加油,虽然看不懂,还是支持你!
页: [1]
查看完整版本: 安路SparkRoad串口转localbus实现指令控制