安路SF1系列FPGA(一)两种方式点灯与串口通信-2
<div class='showpostmsg'> 本帖最后由 瓜弟 于 2023-3-1 00:39 编辑<p>在上篇文章中,介绍了SF1系列FPGA的工程建立、下载调试等方法。本文将继续在上一篇的基础上介绍串口通信的使用和使用AHB总线进行LED点灯。</p>
<p><span style="font-size:16px;">1、串口通信的使用</span></p>
<p> 实际上,在创建的软件工程里,system_nuclei.c文件里,函数void _init(void)已经进行了串口的初始化,但是会发现在使用中,上位机出现乱码。需要在system_nuclei.c文件中,更改SYSTEM_CLOCK宏定义,将默认的80MHz更改为实际中PLL输出的频率100MHz。</p>
<p></p>
<p> 在main.c中添加如下串口输出代码。理想情况下,串口会每秒输出一次。但是我们在串口上位机中设为波特率为115200后,会发现,接收到的仍是乱码,如果我们将波特率更改为57600(115200的一半)后,会发现能正常接收不乱码了,这是为何?</p>
<pre>
<code class="language-cpp">#include <stdio.h>
#include "nuclei_sdk_hal.h"
#include "anl_printf.h"
#include "nuclei_uart.h"
int main(void)
{
gpio_enable_output(GPIO, 1);
gpio_enable_input(GPIO, 2);
while(1)
{
if(gpio_read(GPIO, 2) != 0)
{
gpio_toggle(GPIO, 1);
delay_1ms(1000);
printf("anlogic\n");
}
}
}</code></pre>
<p>那就是串口外设的工作频率不是100MHz,在《TN817_SF1 MCU用户指南》中4.3章节中,可知,串口外设的工作频率与CPU工作频率一致,那么到底是多少?且为什么delay_1ms(1000)函数又能准确执行呢?因为delay_1ms使用滴答定时器实现,而滴答定时器的时钟输入又是单独的25MHz,在上篇文章中,RISCV核的例化使用了两个时钟,一个为100MHz的主时钟,另一个则是由晶振时钟引脚输入而来的25MHz。那么CPU到底实际工作频率是多少呢?在startup_anlogic.S文件中,有如下代码</p>
<p> 其中CLK_REG_ADDR的定义在riscv_encoding.h文件中,值为0xE0000010,在《TN817_SF1 MCU用户指南》中,可以查到该地址的寄存器如下</p>
<p> </p>
<p>对比代码可知,此处进行了二分频,CPU、串口工作的频率实际上只有50MHz。那么将启动代码中的0x83改为0x81即可正常使用。</p>
<p> </p>
<p><span style="font-size:16px;">2、使用AHB总线读写寄存器进行点灯</span></p>
<p> AHB是属于AMBA下的一个片内总线协议,为多主多从结构,通过将在FPGA侧的硬件代码依据AHB接口协议作为从机挂载在AHB总线上,并在硬件代码中指定地址,即可实现由RISCV-V硬核的软件代码通过指针对目标硬件代码功能的访问与控制。常用的片内总线还有AXI、APB、wishbone等,其中常见的是AXI、AHB、APB,wishbone常见于开源项目,在opencores.org可见许多项目使用wishbone。</p>
<p> SF1器件的AHB总线的可用地址范围未在数据手册中找到,根据官方的参考例程mcu_ahb_to_fpga,以及《TN817_SF1 MCU用户指南》猜测AHB的可用地址范围为0x4000_0000 ~ 0x5FFF_FFFF。可能由于可用空间较小,RISCV的AHB接口未提供HSELx片选信号,所以在实现过程中,不能使用HSELx信号作为状态机启动标志。且当有多个模块连接值AHB总线时,HREADY、HRESP、HRDATA等输出信号需要注意三态处理,本文仅有有一个模块使用AHB总线,故常规处理。</p>
<p> 关于AHB总线的时许本文不再讲解,各种资料太多,但仍推荐阅读AMBA标准文档。本文在上一篇的基础上,使用一个reg型变量存储来自AHB写入的控制量,该变量将连接至LED的引脚(H5),地址为0x43214320,通过对该寄存器写0或1,实现对LED的点灯控制。代码如下</p>
<pre>
<code>module AHB_LED
#(
parameter LED_ADDR = 32'h43214320
)
(
input wire HCLK,
input wire HRSTn,
input wire HTRANS,
input wire HADDR,
input wire HWRITE,
input wire HSIEZ,
input wire HBURST,
input wire HPROT,
input wire HWDATA,
output reg HRDATA,
output reg HREADY,
output reg HRESP,
output reg LED
);
parameter HTRANS_IDLE = 2'b00; //Slave忽略掉此时的传输
parameter HTRANS_BUSY = 2'b01; //表示master正在处理数据,slave需要忽略掉此时的传输
parameter HTRANS_NONSEQ = 2'b10; //表明当前是单笔的数据,或者是Burst的第一笔数据
parameter HTRANS_SEQ = 2'b11; //是Burst传输的剩余数据
parameter HBURST_SINGLE = 3'b000; //单笔数据传输
parameter HBURST_INCR = 3'b000; //不定长递增方式批量传输
parameter HBURST_WRAP4 = 3'b000; //4个数据回绕方式批量传输
parameter HBURST_INCR4 = 3'b000; //4个数据递增方式批量传输
parameter HBURST_WRAP8 = 3'b000; //8个数据回绕方式批量传输
parameter HBURST_INCR8 = 3'b000; //8个数据递增方式批量传输
parameter HBURST_WRAP16 = 3'b000; //16个数据回绕方式批量传输
parameter HBURST_INCR16 = 3'b000; //16个数据递增方式批量传输
parameter HREADY_REDY = 1'b1;
parameter HREADY_BUSY = 1'b0;
parameter HRESP_OKEY = 2'b00; //传输完成
parameter HRESP_ERROR = 2'b01; //传输错误
parameter HRESP_RETRY = 2'b10; //传输未完成,请求主设备重新开始一个传输,
//arbiter会继续使用通常的优先级
parameter HRESP_SPLIT = 2'b11; //传输未完成,请求主设备分离一次传输,
//arbiter会调整优先级方案以便其他请求总线的主设备可以访问总线
parameter HSIZE_8b = 3'b000;
parameter HSIZE_16b = 3'b001;
parameter HSIZE_32b = 3'b010;
parameter HSIZE_64b = 3'b011;
parameter HSIZE_128b = 3'b100;
parameter HSIZE_256b = 3'b101;
parameter HSIZE_512b = 3'b110;
parameter HSIZE_1024b = 3'b111;
parameter HWRITE_WR = 1'b1;
parameter HWRITE_RD = 1'b0;
parameter FSM_IDLE = 4'h0;
//parameter FSM_ADDR = 4'h1;
parameter FSM_WR = 4'h2;
parameter FSM_RD = 4'h4;
reg FSM_Current;
reg FSM_Next;
always@(posedge HCLK or negedge HRSTn)
begin
if(HRSTn == 0)
begin
FSM_Current <= FSM_IDLE;
end
else
begin
FSM_Current <= FSM_Next;
end
end
always @(*) begin
if(HRSTn == 0)
begin
FSM_Next <= FSM_IDLE;
end
else
begin
case(FSM_Current)
FSM_IDLE:
begin
if( (HTRANS == HTRANS_NONSEQ) && (HADDR == LED_ADDR) )
begin
if(HWRITE == HWRITE_WR)
begin
FSM_Next <= FSM_WR;
end
else
begin
FSM_Next <= FSM_RD;
end
end
else
begin
FSM_Next <= FSM_IDLE;
end
end
FSM_WR:
begin
FSM_Next <= FSM_IDLE;
end
FSM_RD:
begin
FSM_Next <= FSM_IDLE;
end
default:
begin
FSM_Next <= FSM_IDLE;
end
endcase
end
end
always@(posedge HCLK or negedge HRSTn)
begin
if(HRSTn == 0)
begin
LED <= 1'b0;
HRDATA <= 32'hZ;
HREADY <= HREADY_REDY;
HRESP <= HRESP_OKEY;
end
else
begin
case(FSM_Current)
FSM_IDLE:
begin
if( (HTRANS == HTRANS_NONSEQ) && (HADDR == LED_ADDR) )
begin
LED <= LED;
HRDATA <= 32'hZZZZ_ZZZZ;
HREADY <= HREADY_BUSY;
HRESP <= HRESP_OKEY;
end
else
begin
LED <= LED;
HRDATA <= 32'h0;
HREADY <= HREADY_REDY;
HRESP <= HRESP_OKEY;
end
end
FSM_WR:
begin
LED <= HWDATA;
HRDATA <= 32'h0;
HREADY <= HREADY_REDY;
HRESP <= HRESP_OKEY;
end
FSM_RD:
begin
LED <= LED;
HRDATA <= {31'h0, LED};
HREADY <= HREADY_REDY;
HRESP <= HRESP_OKEY;
end
default:
begin
LED <= 1'b0;
HRDATA <= 32'hZ;
HREADY <= HREADY_REDY;
HRESP <= HRESP_OKEY;
end
endcase
end
end
endmodule</code></pre>
<p>在原有的代码的基础上,例化RISCV硬核时,将AHB总线信号例化,代码如下</p>
<pre>
<code>module SOC(
input wire clk,
input wire clk_low,
input wire rstn,
input wire jtag_tck,
output wire jtag_tdo,
input wire jtag_tms,
input wire jtag_tdi,
input wire uart_rx,
output wire uart_tx,
inout wire gpio_0,
inout wire gpio_1,
output wire htrans,
output wire hwrite,
output wire haddr,
output wire hsize,
output wire hburst,
output wire hprot,
output wire hmastlock,
output wire hwdata,
input wire hclk,
input wire hrdata,
input wire hresp,
input wire hready
);
wire ps_gpio0_out;
wire ps_gpio0_dir;
wire ps_gpio0_in ;
wire ps_gpio1_out;
wire ps_gpio1_dir;
wire ps_gpio1_in ;
gpio u1(
.gpio_dir(ps_gpio0_dir),
.gpio_out(ps_gpio0_out),
.gpio_in(ps_gpio0_in),
.pin(gpio_0)
);
gpio u2(
.gpio_dir(ps_gpio1_dir),
.gpio_out(ps_gpio1_out),
.gpio_in(ps_gpio1_in),
.pin(gpio_1)
);
RISCV u3(
.core_clk(clk),
.core_reset(rstn),
.timer_clk ( clk_low ),
.jtag_tck ( jtag_tck),
.jtag_tdo ( jtag_tdo),
.jtag_tms ( jtag_tms),
.jtag_tdi ( jtag_tdi),
.soft_ip_apbm_en( 1'b0 ),
.qspi0cfg1_mode ( 1'b1 ),
.qspi0cfg2_mode ( 1'b1 ),
.uart_tx ( uart_tx),
.uart_rx ( uart_rx),
.gpio0_out ( ps_gpio0_out),
.gpio0_dir ( ps_gpio0_dir),
.gpio0_in ( ps_gpio0_in ),
.gpio1_out ( ps_gpio1_out),
.gpio1_dir ( ps_gpio1_dir),
.gpio1_in ( ps_gpio1_in ),
.htrans ( htrans ),
.hwrite ( hwrite ),
.haddr ( haddr ),
.hsize ( hsize ),
.hburst ( hburst ),
.hprot ( hprot ),
.hmastlock ( hmastlock ),
.hwdata ( hwdata ),
.hclk ( hclk ),
.hrdata ( hrdata ),
.hresp ( hresp ),
.hready ( hready )
);
endmodule
</code></pre>
<p>配置PLL,使其输出第二路时钟信号50MHz,作为AHB总线的时钟频率</p>
<p> </p>
<p>在TOP层,将各模块组装起来</p>
<pre>
<code>module TOP(
input wire clk_25MHz,
input wire rstn,
input wire jtag_tck,
output wire jtag_tdo,
input wire jtag_tms,
input wire jtag_tdi,
input wire uart_rx,
output wire uart_tx,
inout wire gpio_0,
inout wire gpio_1,
output wire LED
);
wire clk_100MHz;
wire hclk;
pll U1(
.refclk(clk_25MHz),
.reset(~rstn),
.clk0_out(clk_100MHz),
.clk1_out(hclk)
);
wire htrans;
wire hwrite;
wire haddr;
wire hsize;
wire hburst;
wire hprot;
wire hmastlock;
wire hwdata;
wire hrdata;
wire hresp;
wire hready;
SOC U2(
.clk(clk_100MHz),
.clk_low(clk_25MHz),
.rstn(~rstn),
.jtag_tck(jtag_tck),
.jtag_tdo(jtag_tdo),
.jtag_tms(jtag_tms),
.jtag_tdi(jtag_tdi),
.uart_rx(uart_rx),
.uart_tx(uart_tx),
.gpio_0(gpio_0),
.gpio_1(gpio_1),
.htrans ( htrans ),
.hwrite ( hwrite ),
.haddr ( haddr ),
.hsize ( hsize ),
.hburst ( hburst ),
.hprot ( hprot ),
.hmastlock ( hmastlock ),
.hwdata ( hwdata ),
.hclk ( hclk ),
.hrdata ( hrdata ),
.hresp ( hresp ),
.hready ( hready )
);
AHB_LED U3(
//ports
.HCLK ( hclk ),
.HRSTn ( rstn ),
.HTRANS ( htrans ),
.HADDR ( haddr ),
.HWRITE ( hwrite ),
.HSIEZ ( hsize ),
.HBURST ( hburst ),
.HPROT ( hprot ),
.HWDATA ( hwdata ),
.HRDATA ( hrdata ),
.HREADY ( hready ),
.HRESP ( hresp ),
.LED ( LED )
);
endmodule
</code></pre>
<p>经过综合后,补充LED引脚的约束</p>
<pre>
<code>set_pin_assignment { LED } { LOCATION = H5; }
set_pin_assignment { clk_25MHz } { LOCATION = D7; }
set_pin_assignment { gpio_0 } { LOCATION = J5; }
set_pin_assignment { gpio_1 } { LOCATION = H3; }
set_pin_assignment { jtag_tck } { LOCATION = C7; }
set_pin_assignment { jtag_tdi } { LOCATION = D5; }
set_pin_assignment { jtag_tdo } { LOCATION = C6; }
set_pin_assignment { jtag_tms } { LOCATION = D6; }
set_pin_assignment { rstn } { LOCATION = H2; }
set_pin_assignment { uart_rx } { LOCATION = E4; }
set_pin_assignment { uart_tx } { LOCATION = A4; }</code></pre>
<p>以上就完成了硬件设计,软件设计很简单,对0x43214320地址进行写操作即可,代码如下</p>
<pre>
<code class="language-cpp">#include <stdio.h>
#include "nuclei_sdk_hal.h"
#include "anl_printf.h"
#include "nuclei_uart.h"
int main(void)
{
gpio_enable_output(GPIO, 1);
gpio_enable_input(GPIO, 2);
unsigned char st = 1;
while(1)
{
if(gpio_read(GPIO, 2) != 0)
{
gpio_toggle(GPIO, 1);
delay_1ms(1000);
*((unsigned long *)0x43214320) = st;
st = ~st;
printf("anlogic\n");
}
}
}
</code></pre>
<p>代码编译完成后,按照上一篇文章的教程,下载完成后,就可见H5引脚对应的LED的闪烁</p>
<p>8037fe709ea4acc90b5d461b98c54c8b</p>
<p> </p>
<p>附件为硬件与软件两个完整工程:</p>
<p> </p>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> <p>串口通信的使用和使用AHB总线进行LED点灯效果还行</p>
Jacktang 发表于 2023-3-8 07:38
串口通信的使用和使用AHB总线进行LED点灯效果还行
<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/congra.gif" width="48" /></p>
<p>我启动文件中更改为0x81后,会导致自己绑定的复位按键无法正常复位</p>
页:
[1]