例说FPGA:可直接用于工程项目的第一手经验【2.2】

例说FPGA:可直接用于工程项目的第一手经验【2.2】

6.4 软件程序解析

1.main.c源文件软件程序解析

main.c的函数列表如表6-3所示。

int main(void)函数
该函数在外设初始化后,通过JTAG UART打印一串初始化信息;接着依次对Flash的1023块进行擦除、写入并打印所有写入数据、读出并打印所有读出数据;最后LED不停地闪烁。
该函数依次执行以下操作。
①打印初始化信息。
②擦除Flash的第1023块数据。
③读取并打印Flash第1023块首页数据。
④产生一组数据写入到Flash第1023块首页中。
⑤再次读取并打印Flash第1023块首页数据。
⑥LED循环闪烁。
2.flash.c源文件软件程序解析
flash.c的函数列表如表6-4所示。

(1)void Flash_page_write(alt_u32 fpage,alt_u16 write_data_num)函数
该函数实现Flash的页写操作。该函数有两个入口参数,fpage表示写入Flash的页地址,write_data_num表示写入数据字节数。flashdb为2048字节的全局数组,在调用该函数前,需要将写
入Flash的数据预先缓存到该数组中。
该函数依次执行以下操作。
①读取状态寄存器,判断并等待Flash处于忙状态。②送Flash操作页地址。
③开启写使能。
④连续写数据(1~2048 B)。
⑤关闭写使能,结束操作。
(2)void Flash_page_mcuread(alt_u32 fpage,alt_u16 write_data_num)函数
该函数实现Flash的页读操作。该函数有两个入口参数,fpage表示写入Flash的页地址,write_data_num表示读出数据字节数。flashdb为2048字节的全局数组,在调用该函数后,Flash中读
出的数据将被缓存到该数组中。
该函数依次执行以下操作。
①读取状态寄存器,判断并等待Flash处于忙状态。
②送Flash操作页地址。
③开启读使能。
④读取状态寄存器,判断并等待Flash处于忙状态。
⑤连续读数据(1~2048 B)。
⑥关闭读使能,结束操作。
(3)void Flash_block_erase(alt_u16 fblock)函数
该函数实现Flash的块擦除操作。该函数只有一个入口参数,fblock表示Flash的擦除块地址。
该函数依次执行以下操作。
①读取状态寄存器,判断并等待Flash处于忙状态。
②送操作块地址。
③开启擦除操作。
④稍作延时(1μs即可)。
⑤结束擦除操作。
6.5 板级调试
①打开“http://www.hzcourse.com/resource/readBook?path=/openresources/teach_ebook/uncompressed/15714/OEBPS/Text/...\prj\vip_ex4”下的工程。
②使用Programmer将“http://www.hzcourse.com/resource/readBook?path=/openresources/teach_ebook/uncompressed/15714/OEBPS/Text/...\prj\vip_ex4\output_files”文件夹
下的vip.sof文件下载到VIP核心板中。
③打开EDS软件。导入“http://www.hzcourse.com/resource/readBook?path=/openresources/teach_ebook/uncompressed/15714/OEBPS/Text/...\prj\vip_ex4\software”文件夹下
的软件工程(包括应用工程和BSP工程)。

④运行应用软件,片刻后,可以看到Nios II Console开始打印如图6-45和图6-46所示的数据。完成打印后LED指示灯D1开始闪烁。在打印窗口中,我们可以看到本实例的软件执行了4个主要操作。

·擦除NAND Flash的第1023 Block的数据。

·读出刚刚执行完擦除操作的NAND Flash的第1023 Block的第1个Page数据。刚擦除完,都是0xff的数据。

·产生一组0到255递增的数据写入到NAND Flash的第1023 Block的第1个Page中。

·写入完成后,读出这个Page的数据,以此确认写操作是否正确执行。

第7章 工程实例5——多分辨率VGA显示驱动

本章导读

本章的工程实例设计一个简单的VGA驱动控制器,产生Color bar。有VGA基本知识的讲解、VGA驱动时序产生的原理讲解、代码解析以及与本工程实例相关的FPGA配置引脚复用设置。尽管是一个简单的例程,但是我们将仍然不遗余力地将更多的知识点传授给读者。

7.1 功能概述

VGA(Video Graphics Array)即视频图形阵列,是IBM在1987年随PS/2(PS/2原是“Personal System 2”的意思,“个人系统2”,是IBM公司在1987年推出的一种个人电脑。PS/2电脑上使用的键盘鼠标接口就是现在的PS/2接口。因为标准不开放,PS/2电脑在市场中失败了。只有PS/2接口一直沿用到今天)一起推出的使用模拟信号的一种视频传输标准,在当时具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。这个标准对于现今的个人电脑市场已经十分过时。即使如此,VGA仍然是最多制造商所共同支持的一个标准,个人电脑在加载自己的独特驱动程序之前,都必须支持VGA的标准。例如,微软Windows系列产品的开机画面仍然使用VGA显示模式,这也说明其在显示标准中的重要性和兼容性。

VGA最早指的是显示器640×480这种显示模式。(而今天的VGA其实已经不仅仅局限于640×480这种分辨率了,通常情况下,各种各样适用于VGA接口传输的分辨率都可以统称为VGA。当然了,严格来讲,每个分辨率都会有自己的叫法,如800×600就称作SVGA)。VGA接口如图7-1所示。

驱动VGA显示的接口,主要有以下3种信号:行同步信号HSYNC、场同步信号VSYNC和3条色彩电压传输信号(R、G、B分别对应)。色彩信号的电压为0~0.7V,其同步是靠前面两个信号来协助的。至于HSYNC、VSYNC和色彩信号之间以什么样的关系进行传输,这都是相对固定的,虽然VGA收发双方没有时钟信号做同步,但我们通常会约定发送方有一个基本的时钟,VSYNC、HSYNC和色彩信号都会按照这个时钟的节拍来确定状态。

VGA的接口时序如图7-2所示,场同步信号VSYNC在每帧(即送一次全屏的图像)开始的时候产生一个固定宽度的高脉冲,行同步信号HSYNC在每行开始的时候产生一个固定宽度的高脉冲,色彩数据在某些固定的行和列交汇处有效。

图7-2 VGA驱动基本时序

如前所述,我们通常以一个基准时钟驱动VGA信号的产生,用这个基准时钟为时间单位来产生的时序如图7-3所示。

对于一个刷新频率为60Hz,分辨率为640×480的标准VGA显示驱动,若它的基准驱动时钟为25MHz,则它的计数脉冲参数如表7-1所示。注意列的单位为“行”,而行的单位为“基准时钟周期数”,即25MHz时钟脉冲数。

对于一个刷新频率为72Hz,分辨率为800×600的SVGA显示驱动,若它的基准驱动时钟为50MHz,它的计数脉冲参数如表7-2所示。注意列的单位为“行”,而行的单位为“基准时钟周期数”,即50MHz时钟脉冲数。

对于一个刷新频率为60Hz,分辨率为1024×768的显示驱动,若它的基准驱动时钟为65MHz,则它的计数脉冲参数如表7-3所示。注意列的单位为“行”,而行的单位为“基准时钟周期数”,即65MHz时钟脉冲数。

对于一个刷新频率为60Hz,分辨率为1280×960的显示驱动,若它的基准驱动时钟为108MHz,则它的计数脉冲参数如表7-4所示。注意列的单位为“行”,而行的单位为“基准时钟周期数”,即108MHz时钟脉冲数。

对于一个刷新频率为60Hz,分辨率为1280×1024的显示驱动,若它的基准驱动时钟为108MHz,则它的计数脉冲参数如表7-5所示。注意列的单位为“行”,而行的单位为“基准时钟周期数”,即108MHz时钟脉冲数。

对于一个刷新频率为60Hz,分辨率为1920×1080的显示驱动,若它的基准驱动时钟为130MHz,则它的计数脉冲参数如表7-6所示。注意列的单位为“行”,而行的单位为“基准时钟周期数”,即130MHz时钟脉冲数。

了解了VGA的基本驱动原理,我们还要回来看看实际驱动电路的工作原理。由于FPGA接口都是数字信号,无法直接输出VGA色彩信号所需的0~0.7V模拟电压,所以需要使用专门的DAC芯片进行转换。我们的SF-VGA子板就板载了一颗专用的3路DAC转换芯片ADV7123。

本实例连接VIP1核心板和SF-VGA子板进行实验。VGA驱动时序产生模块通过宏定义实现VGA/SVGA/720p/1080p多分辨率显示驱动,功能框图如图7-4所示。

7.2 装配说明

VIP1核心板的OUTPPN插座P4连接到SF-VGA子板的P1。SF-VIP1核心板和SF-VGA子板的连接示意如图7-5所示。

7.3 复用引脚设置

在该实例中,SF-VGA子板连接VIP核心板的插座P4。在分配好引脚后,若直接进行编译,则将会出现如图7-6所示的错误。

这是怎么回事?为什么Pin_F16有“multiple pins assigned to”,即多个信号分配给这个引脚?如图7-7所示,pin assignment中并没有多个信号分配给Pin_F16这个引脚,只有adv7123_sync_n分配给了它。

再来看看如图7-8所示的原理图,这里的F16引脚,FPGA的功能定义是“IO,DIFFIO_R4n,(nCEO)”。问题就在这个“nCEO”上,这是个配置相关的引脚,默认情况下Quartus II工具认为它作为配置功能使用,如果用户将它分配到信号上,则编译时必定报错。

那么这个错误如何规避呢?很简单,大家在Quartus II菜单中执行Assignments→Device命令,进入Device界面,如图7-9所示。找到“Device and Pin Options”按钮,单击它。

此时弹出如图7-10所示的界面,在Category下面的列表框中选择“Dual-Purpose Pins”,然后找到右侧的“nCEO”一栏,默认的设置是“Use as programming pin”,双击它,将其改为“Use as regular I/O”。

重新编译工程后不会再有错误信息。同样的,如果使用“Dual-Purpose Pins”遇到类似的问题,可以通过类似的方式解决。

7.4 Verilog代码解析

本实例有4个模块(sys_ctrl.v和其下的pll_controller.v作为一个模块),2个层级,其层次结构如图7-11所示。

图7-11 工程实例5代码层次图
·vip.v是顶层模块,其下例化了3个子模块,即sys_ctrl.v模块、vag_ctrl.v模块和led_controller.v模块。该模块仅仅用于子模块间的接口连接,以及连接到FPGA外部接口的定义,该模块中未作任
何的逻辑处理。
·sys_ctrl.v二级子模块中例化了PLL模块,并且对输入PLL的复位信号以及PLL锁定后的复位信号进行“异步复位,同步释放”的处理,确保系统的复位信号稳定可靠。
·vga_ctrl.v二级子模块产生多分辨率VGA显示驱动逻辑。
·led_controller.v二级子模块进行24位计数器的循环计数,产生分频信号用于实现LED指示灯的闪烁。
1.Vip.v模块代码解析
略。
2.Sys_ctrl.v模块代码解析
略。
3.Led_controller.v模块代码解析
略。
4.vga_ctrl.v模块代码详解
vga_ctrl.v模块的设计代码产生最基本的VGA驱动和显示测试。代码中,因为我们的电路中使用了3路DAC驱动的芯片ADV7123进行VGA色彩信号电压的产生,该芯片的驱动有两个用于同步数
据的信号也需要在接口中产生相应的时序。简单来看,控制这颗DAC芯片的也就是一个用于数据锁存的时钟以及两个同步信号,还有就是3组要送给VGA显示器的色彩数据R、G、B信号的数字
值,它们之间只要产生一组符合芯片手册要求的时序即可完成3路VGA所需的色彩电压。此外,为了让代码能够更好地在不同分辨率之间移植切换,我们使用了预编译指令ifdef语法,其实和C语言
的预编译指令用法基本类似。vga_ctrl.v模块的逻辑设计功能框图如图7-12所示。

①从接口来看,vga_ctrl.v模块可以分为两大类,一类是时钟和复位信号,时钟都是PLL处理后输出的,如下所示。
input clk_25m;
input clk_50m;
input clk_65m;
input clk_108m;
input clk_130m;
input rst_n;
另一类接口是ADV7123芯片的接口信号,ADV7123是一颗3路DAC芯片,用于将数字信号转换为VGA可以接收的RGB模拟电平。接口如下所示。
output[4:0] vga_r;
output[5:0] vga_g;
output[4:0] vga_b;
output reg vga_hsy,vga_vsy;
output vga_clk;
output adv7123_blank_n;
output adv7123_sync_n;
②我们先来看时钟信号,驱动ADV7123的同步时钟只需要一个,但为何引了这么多的时钟源进来呢?我们不妨先看看源代码。//-----------------------------------------------------------
wire clk;
assign vga_clk = clk;
//-----------------------------------------------------------
//`define VGA_640_480
//`define VGA_800_600
//`define VGA_1024_768
//`define VGA_1280_960
//`define VGA_1280_1024
`define VGA_1920_1080
//-----------------------------------------------------------
`ifdef VGA_640_480
//VGA Timing 640*480 & 25MHz & 60Hz
assign clk = clk_25m;
……
`endif
`ifdef VGA_800_600
//VGA Timing 800*600 & 50MHz & 72Hz
assign clk = clk_50m;
……
`endif
`ifdef VGA_1024_768
//VGA Timing 1024*768 & 65MHz & 60Hz
assign clk = clk_65m;
……
`endif
`ifdef VGA_1280_960
//VGA Timing 1280*1024 & 108MHz & 60Hz
assign clk = clk_108m;
……
`endif
`ifdef VGA_1280_1024
//VGA Timing 1280*1024 & 108MHz & 60Hz
assign clk = clk_108m;
……
`endif
`ifdef VGA_1920_1080
//VGA Timing 1920*1080 & 130MHz & 60Hz
assign clk = clk_130m;
……
`endif
这里的宏定义“`define VGA_1920_1080”,使得随后的众多“`ifdefhttp://www.hzcourse.com/resource/readBook?
path=/openresources/teach_ebook/uncompressed/15714/OEBPS/Text/...`endif”语句只有一个有效,即:
`ifdef VGA_1920_1080
//VGA Timing 1920*1080 & 130MHz & 60Hz
assign clk = clk_130m;
……
`endif
因此,“assign vga_clk=clk;”等效于“assign vga_clk=clk_130m;”。同理,若在如下宏定义中,任意且只有一个有效的情况下,响应
的“`ifdefhttp://www.hzcourse.com/resource/readBook?path=/openresources/teach_ebook/uncompressed/15714/OEBPS/Text/...`endif”语句则有效,那么时钟源就由此决定了。
//`define VGA_640_480
//`define VGA_800_600
//`define VGA_1024_768
//`define VGA_1280_960
//`define VGA_1280_1024
此外,在“`ifdefhttp://www.hzcourse.com/resource/readBook?path=/openresources/teach_ebook/uncompressed/15714/OEBPS/Text/...`endif”语句中定义的其他参数也由此确
定。
③如图7-13所示,对于一个液晶屏来说,我们传输数据不可能一下全都送过去,没有那么大的带宽。我们只能一个像素点一个像素点地送色彩数据,从方向上来看,就是从左到右(x轴方
向),从上到下(y轴方向)。

因此,我们的代码中定义了两个12位的计数器,即x轴计数器xcnt和y轴计数器ycnt。xcnt随主时钟不停地计数,ycnt在xcnt计数器完成一个周期计数时才会递增计数,它也有自己的计数周
期。xcnt和ycnt的计数周期值都是由前面的参数定义好的。xcnt和ycnt计数器的计数逻辑代码如下所示。
//-----------------------------------------------------------
//x and y counter
reg[11:0] xcnt;
reg[11:0] ycnt;
always @(posedge clk or negedge rst_n)
if(!rst_n) xcnt <= 12'd0;
else if(xcnt >= VGA_HTT) xcnt <= 12'd0;
else xcnt <= xcnt+1'b1;
always @(posedge clk or negedge rst_n)
if(!rst_n) ycnt <= 12'd0;
else if(xcnt == VGA_HTT) begin
if(ycnt >= VGA_VTT) ycnt <= 12'd0;
else ycnt <= ycnt+1'b1;
end
else ;
④如图7-14所示,实际的数据传输方式不是仅仅只有x轴和y轴的有效显示图像的数据,而是在每一行或每一场(即一整个屏幕的图像帧)的开始和结束都有一些空闲的时间,这个时间内的数
据不显示在屏幕上,可以用来产生一些同步信号,避免行、场的错乱。

从实现上来看,行同步信号vga_hsy和场同步信号vga_vsy分别在x轴计数和y轴计数的空闲时间产生一个高脉冲,以此达到同步的目的。逻辑代码如下。
//-----------------------------------------------------------
//hsy and vsy generate
always @(posedge clk or negedge rst_n)
if(!rst_n) vga_hsy <= 1'b0;
else if(xcnt < VGA_HST) vga_hsy <= 1'b1;
else vga_hsy <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n) vga_vsy <= 1'b0;
else if(ycnt < VGA_VST) vga_vsy <= 1'b1;
else vga_vsy <= 1'b0;
⑤对于显示的有效区域,即x轴和y轴整个计数周期相互交叉的有效区域内,我们是需要送像素色彩给显示器的,显示有效区域标志信号产生的逻辑代码如下。
//-----------------------------------------------------------
//vga valid signal generate
reg vga_valid;
always @(posedge clk or negedge rst_n)
if(!rst_n) vga_valid <= 1'b0;
else if((xcnt >= (VGA_HST+VGA_HBP)) && (xcnt < (VGA_HST+VGA_HBP+VGA_HVT))
&& (ycnt >= (VGA_VST+VGA_VBP)) && (ycnt < (VGA_VST+VGA_VBP+VGA_VVT)))
vga_valid <= 1'b1;
else vga_valid <= 1'b0;
assign adv7123_blank_n = vga_valid;
assign adv7123_sync_n = 1'b0;
⑥余下的逻辑在液晶屏有效显示区域内产生一个color bar的效果,通过判断不同的x和y坐标位置,送相应的色彩值来实现。
7.5 板级调试
①连接好硬件,VIP核心板+SF-VGA子板+VGA显示器(通用电脑显示器),并且给VIP核心板上电。
②打开“http://www.hzcourse.com/resource/readBook?path=/openresources/teach_ebook/uncompressed/15714/OEBPS/Text/...\prj\vip_ex5”文件夹下的Quartus II工程。
③使用Programmer将“http://www.hzcourse.com/resource/readBook?path=/openresources/teach_ebook/uncompressed/15714/OEBPS/Text/...\prj\vip_ex5\output_files”文件夹
下的vip.sof文件下载到VIP核心板中,此时VIP板上的指示灯D1开始闪烁。并且在电脑显示器上出现了Color Bar,默认代码的显示驱动分辨率是1920×1080,大家可以尝试更改vga_ctrl.v模块中的
宏定义,使用不同分辨率驱动液晶屏显示。1080p的显示效果如图7-15所示。

Read more

OpenTiny NEXT 前端智能化系列直播征文开启,带你系统学习 AI 前端与 WebAgent

OpenTiny NEXT 前端智能化系列直播征文开启,带你系统学习 AI 前端与 WebAgent

🔥 个人主页:杨利杰YJlio❄️ 个人专栏:《Sysinternals实战教程》《Windows PowerShell 实战》《WINDOWS教程》《IOS教程》《微信助手》《锤子助手》《Python》《Kali Linux》《那些年未解决的Windows疑难杂症》🌟 让复杂的事情更简单,让重复的工作自动化 文章目录 * 在这里插入图片描述 1. AI 前端,不该只是“把聊天框接到页面里” * 在这里插入图片描述 2. 这次活动,为什么我觉得值得参加 * 2.1 不只是听概念,而是逼着自己把概念落地 * 2.2 技术范围很新,但切入点并不空泛 * 2.3 对写作者也很友好 * 在这里插入图片描述 3. 我理解的“前端智能化”,到底在变什么 * 3.1 第一层:前端从“固定界面”走向“

前端 + agent 开发学习路线

背景:团队启动Agent项目,从零开始学习工程化AI开发 感谢ai老师写的学习指南。存档! 引言:从困惑到清晰 最近团队要启动Agent项目,我第一次接触这个概念时,只停留在“接入大模型API+优化Prompt”的浅层理解。经过大量学习和实践探索,我才发现工程化Agent开发是系统化的架构设计,而不仅仅是API调用。 这篇文章记录我从前端视角出发,探索Agent工程化开发的学习路径和实践经验。如果你也是前端/全栈开发者,想要在AI时代找到自己的定位,这篇指南应该能帮到你。 一、认知重塑:什么是工程化Agent? 1.1 我的错误认知 vs 现实 我原来的理解: Agent = 大模型API + Prompt优化 实际上的工程化Agent: Agent = 系统架构 + 可控执行 + 安全审查 + 领域适配 + 可观测性 1.2 Agent的分层架构(医疗场景示例) 你的主战场 任务分解器 工具路由器 记忆管理器 状态监控器

OpenClaw接入模型并基于WebUI完成智能操作

OpenClaw接入自定义模型并基于WebUI完成智能操作 背景介绍 OpenClaw(原 Clawdbot)是一个开源的 AI 代理框架,支持通过配置文件或 GUI 界面进行灵活配置。安装 OpenClaw 后,用户可以通过修改工作目录下的配置文件 openclaw.json 来接入不同的 LLM 模型提供商。 OpenClaw 支持众多主流模型提供商,包括 OpenAI、Anthropic、Moonshot AI(Kimi)、OpenRouter、Vercel AI Gateway、Amazon Bedrock 等。完整的提供商目录可参考官方文档 模型提供商快速入门。 要使用自定义的提供商,需要通过 models.providers 配置进行设置。这种方式允许用户接入官方支持列表之外的其他兼容 OpenAI API 或 Anthropic 格式的模型服务。 接入配置说明 核心配置参数解析

前端大数据导出优化:解决Chrome内存崩溃的实战方案

前端大数据导出优化:解决Chrome内存崩溃的实战方案

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[[email protected]] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? * 专栏导航: 码农阿豪系列专栏导航 面试专栏:收集了java相关高频面试题,面试实战总结🍻🎉🖥️ Spring5系列专栏:整理了Spring5重要知识点与实战演练,有案例可直接使用🚀🔧💻 Redis专栏:Redis从零到一学习分享,经验总结,案例实战💐📝💡 全栈系列专栏:海纳百川有容乃大,可能你想要的东西里面都有🤸🌱🚀 目录 * 前端大数据导出优化:解决Chrome内存崩溃的实战方案 * 引言 * 问题分析 * 1. 为什么 Chrome 会崩溃,而 QQ 浏览器正常? * 2. 常见崩溃场景