南京大学-数字逻辑与计算机组成实验(2024秋)-VGA接口实验(实验六)思路

news/2025/2/3 20:54:48/文章来源:https://www.cnblogs.com/ilfufu/p/18697548

声明:本博客仅供学习参考,请勿作出直接抄袭等违反学术诚信的行为

实验环境

  • 软件:Vivado 2020.2
  • 硬件:Nexys A7-100T开发板

本门课程的实验环境似乎有两种,代码逻辑可能有所不同,请自行注意

实验主要目标

  • 结合键盘模块,按键逻辑正常
  • 屏幕有显示
  • 在前面的基础上,实现删除、退格、清屏等进阶操作
  • 输入特殊字符串,按下回车后显示对应自定义内容

个人思路与实现

前言

首先本次实验附件实现的内容是正确的,不需要你改动,即vgadraw、vgactrl、vgasim的逻辑没有问题,不需要你更改。vgactrl中的参数建议改成640*480比例的,后面实验会方便一点。参数更改如下:

   parameter    H_Sync_Width = 96;parameter    H_Back_Porche = 48;parameter    H_Active_Pixels = 640;parameter    H_Front_Porch = 16;parameter    H_Totals = 800;parameter    V_Sync_Width = 2;parameter    V_Back_Porche = 33;parameter    V_Active_Pixels = 480;parameter    V_Front_Porch = 10;parameter    V_Totals = 525;
总览

然后我们先梳理一下给出的实验文件结构:.coe文件都是静态图像部分的,scancode文件是键盘部分的,clkgen文件是生成时钟的模块(我没有使用),ASC16是字模文件,vgactrl负责生成同步信号和x轴y轴位置,vgadraw是一个函数,根据vgactrl部分的x轴y轴信息生成颜色信号,vgasim模块将vgactrl和vgadraw实例化,然后整理后统一输出VGA的各种信息

梳理完再看实验要求,我们要实现键盘和屏幕的交互,还要实现简单的伪终端行为,还要实现伪应用功能切换显示动态图片/静态图片/文字。所以我们的设计思路如下:以xterm为主文件,和屏幕键盘交互;先更改移植之前的键盘模块,使之能正常输出信号;然后设计实现伪终端界面,包括键盘信号译码、按字母取字模、按字模信号在屏幕上对应位置显示黑白像素等基础操作,以及按下del键、回车键、方向键等的进阶逻辑;然后设计动态图像模块、静态图像模块、文字模块,将其在xterm中实例化,并设计状态机实现终端到这些模块再回到终端的转换

键盘模块

这个前面的实验实现过了,所以没什么好说的,我的实现如下

    module kbd(input fastclk,input PS2_CLK,input PS2_DATA,output val,output [7:0] ascii,output [7:0] ctl,output c50hz);reg [7:0] kb_mem[255:0];initialbegin$readmemh("?/scancode.txt", kb_mem, 0, 255);endreg [20:0]clk_dis = 0;reg clk_50hz = 0;always @(posedge fastclk) begin //100mhzif (clk_dis >= 1000000) begin //50hzclk_dis <= 0;clk_50hz <= ~clk_50hz;end else beginclk_dis <= clk_dis + 1;endendreg [3:0]cnt = 0;reg [7:0]datacur = 0;reg [7:0]dataprev = 0;reg [7:0]dataprepre = 0;reg [7:0]asciicode = 0;reg [20:0]pressrecord = 0;always@(negedge PS2_CLK)begincase(cnt)0:begin end                     1:datacur[0]<=PS2_DATA;2:datacur[1]<=PS2_DATA;3:datacur[2]<=PS2_DATA;4:datacur[3]<=PS2_DATA;5:datacur[4]<=PS2_DATA;6:datacur[5]<=PS2_DATA;7:datacur[6]<=PS2_DATA;8:datacur[7]<=PS2_DATA;9:begin end10:beginif(datacur == 8'hf0 || datacur == 8'he0)begin endelse begin     pressrecord <= pressrecord + 20'd1;dataprepre <= dataprev;dataprev <= datacur;asciicode <= kb_mem[datacur];end         enddefault:begin endendcaseif(cnt<=9) begin cnt<=cnt+1; endelse begin cnt<=0; endendreg shift_pressed = 0;reg cap_pressed = 0;reg [7:0] current_ascii = 0;reg [7:0] ctlcode = 0;reg valid = 0;reg [20:0] recordrecord = 0;always @(posedge clk_50hz) beginif(datacur == dataprev && recordrecord != pressrecord && dataprepre != dataprev) beginendelse if(datacur == dataprev && recordrecord != pressrecord) beginif(datacur == 8'h58)begincap_pressed <= !cap_pressed;endelse if (datacur == 8'h12 || datacur == 8'h59) beginshift_pressed <= 1;endelse begin shift_pressed <= 0;endrecordrecord <= pressrecord;ctlcode <= datacur;if (cap_pressed&&!shift_pressed || shift_pressed&&!cap_pressed) begincurrent_ascii <= asciicode - 8'h20;end else begincurrent_ascii <= asciicode;endvalid <= 1;endelse beginvalid <= 0;endendassign val = valid;assign ascii = current_ascii;assign ctl = ctlcode;assign c50hz = clk_50hz;endmodule
动态图像模块

这块就是实验附件给出的vgasim,在xterm中实例化使用即可,vgasim中实例化了vgactrl和vgadraw文件,记得包括进项目文件

静态图像模块

按实验pdf创建rom的ip核,把你想显示的coe导入进去,然后模仿vgasim的文件结构实例化vgactrl和ip核即可,记得根据你的coe文件更改addr计算方式

    module sim(input CLk,input  BTNC,output [3:0] VGA_R,output [3:0] VGA_G,output [3:0] VGA_B, output  VGA_HS,output  VGA_VS);wire [11:0] cvga_data;wire cvalid;VGACtrl svgactl(.pix_x(),.pix_y(),.hsync(VGA_HS),.vsync(VGA_VS),.pix_valid(cvalid),.pix_clk(CLk),.pix_rst(BTNC));vga_mem my_pic(.clka(CLk),.ena(1'b1),.wea(1'b0),.addra(svgactl.pix_y*640+svgactl.pix_x),.dina(12'd0),.douta(cvga_data));assign VGA_R=cvga_data[11:8];assign VGA_G=cvga_data[7:4];assign VGA_B=cvga_data[3:0];endmodule
文字模块

其具体逻辑和伪终端是相似的,其实就是简化的伪终端,进行初始化后即可进行VGA输出,具体实现原理可以参考下面伪终端模块

伪终端逻辑

首先我们需要一块较大的内存来存储字母信息,这里一般有两种选择:选用分布式ram操作方便,但生成比特流文件需要15-30分钟甚至更长;选用ip核的ram生成比特流速度快,但操作受限。此处我使用的是分布式的ram,读者自行取舍

显存需要进行初始化的操作,若使用ip核可以类比前面的操作,使用coe文件进行初始化;否则需要写一个initial模块初始化一下

    //示例reg [7:0] ascii_mem [0:2399]; // 30 * 80个块integer initi;initial beginascii_mem[0] = 8'h78; // 'x'ascii_mem[1] = 8'h74; // 't'ascii_mem[2] = 8'h65; // 'e'ascii_mem[3] = 8'h72; // 'r'ascii_mem[4] = 8'h6d; // 'm'ascii_mem[5] = 8'h69; // 'i'ascii_mem[6] = 8'h6e; // 'n'ascii_mem[7] = 8'h61; // 'a'ascii_mem[8] = 8'h6c; // 'l'ascii_mem[9] = 8'h2d; // '-'ascii_mem[10] = 8'h2d; // '-'ascii_mem[11] = 8'h2d; // '-'for(initi = 12;initi < 2400;initi = initi+1)beginascii_mem[initi] = 8'h20; // ' 'endendreg [12:0] ascii_mem_index = 80;

然后我们示例化键盘模块,按照实验pdf根据你选择的分辨率实例化时钟ip核模块

    wire clk_25mhz;clk_wiz_0 cw0(.reset(BTNC),.clk_in1(CLK100MHZ),.locked(),.clk_out1(clk_25mhz));

然后就是状态机和键盘操作了,这里我们以伪终端为主体,先把字母信息以ascii码形式存下来,后面再根据不同状态进行选择VGA输出,具体大概如下

    reg [3:0] mode = 0; // 0 terminal 1 dynamic pic 2 static pic 3 txt 4 clearinteger tp;always @ (posedge Keybd.c50hz) beginif(Keybd.val) begin // 键盘信号有效if(mode == 0)begin // 伪终端状态if(Keybd.ascii >= 8'h20) begin // 非功能键ascii_mem[ascii_mem_index]<=Keybd.ascii;ascii_mem_index <= ascii_mem_index +1;endelse begin // 功能键if(Keybd.ctl == 8'h5a)begin // 按下回车ascii_mem[ascii_mem_index] <= 8'h20;if((ascii_mem_index%80) == 7 && ascii_mem[ascii_mem_index - (ascii_mem_index%80)] == 8'h67 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 1] == 8'h72 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 2] == 8'h61 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 3] == 8'h71 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 4] == 8'h68 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 5] == 8'h69 &&ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 6] == 8'h63)beginmode <= 1; // 动态图片endelse if((ascii_mem_index%80) == 5 && ascii_mem[ascii_mem_index - (ascii_mem_index%80)] == 8'h69 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 1] == 8'h6d && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 2] == 8'h61 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 3] == 8'h67 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 4] == 8'h65)beginmode <= 2; // 静态图片endelse if((ascii_mem_index%80) == 3 && ascii_mem[ascii_mem_index - (ascii_mem_index%80)] == 8'h74 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 1] == 8'h78 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 2] == 8'h74)beginmode <= 3; // 文章endelse if((ascii_mem_index%80) == 5 && ascii_mem[ascii_mem_index - (ascii_mem_index%80)] == 8'h63 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 1] == 8'h6c && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 2] == 8'h65 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 3] == 8'h61 && ascii_mem[ascii_mem_index - (ascii_mem_index%80) + 4] == 8'h72)beginmode <= 4;ascii_mem_index <= 80;endelse beginendif(ascii_mem_index>=2320)begin // 超过2400容量时重置位置mode <= 4;ascii_mem_index <= 80;endelse beginascii_mem_index <= ascii_mem_index - (ascii_mem_index%80) + 80; // 否则转到下一行就行endendelse if(Keybd.ctl == 8'h66 || Keybd.ctl == 8'h71)begin // 按下退格或者del键,此处我定义其行为相同,del不具有删除回车功能if(ascii_mem_index%80!=0)beginascii_mem[ascii_mem_index]<=8'h20;ascii_mem[ascii_mem_index-1]<=8'h20;ascii_mem_index <= ascii_mem_index - 1;endendelse if(Keybd.ascii == 8'h12)begin // 按下下方向键ascii_mem[ascii_mem_index]<=asre;ascii_mem_index <= (ascii_mem_index + 80) > 2399 ? ascii_mem_index : ascii_mem_index + 80;endelse if(Keybd.ascii == 8'h18)begin // 按下上方向键ascii_mem[ascii_mem_index]<=asre;ascii_mem_index <= (ascii_mem_index - 80) < 80 ? ascii_mem_index : ascii_mem_index - 80;endelse if(Keybd.ascii == 8'h14)begin // 按下左方向键ascii_mem[ascii_mem_index]<=asre;ascii_mem_index <= ascii_mem_index % 80 == 0 ? ascii_mem_index : ascii_mem_index - 1;endelse if(Keybd.ascii == 8'h16)begin // 按下右方向键ascii_mem[ascii_mem_index]<=asre;ascii_mem_index <= ascii_mem_index % 80 == 79 ? ascii_mem_index : ascii_mem_index + 1;endelse begin endendendelse if(mode == 1 || mode == 2 || mode == 3)begin // 若为其他状态,按下任意按键后回到伪终端mode <= 0;endelse if(mode == 4)begin // 若为重置状态,直接重置mode <= 0;ascii_mem_index <= 80;for(tp = 80;tp<2400;tp = tp+1)beginascii_mem[tp]<=8'h20;endendelse beginmode <= 0;endendelse begin endend

上面我们处理的字母的信息,是以ascii码的形式存在内存中的,现在我们要显示文字,要按ascii码找到字模,按字模的信息在对应位置显示黑白颜色,达到显示文字的效果。首先我们要读入字模信息,这里建议使用二维的寄存器堆[7:0] [0:1535]并更改后面的计算逻辑,使用三维堆大概率报警告无法读入信息(经验之谈)

    reg [7:0] asc96 [0:95] [0:15];initial begin$readmemh("?/ASC16-96.txt", asc96);end

最后就是实例化与选择输出阶段,对伪终端,我们需要套用一下vgactrl获取x轴y轴位置信息,算出现在处在哪个块中,并算出块内偏移,后面按块位置取ascii码,按ascii码取字模,按偏移算出具体点像素是白还是黑

    //mode 0VGACtrl myctl(.pix_clk(clk_25mhz),.pix_rst(BTNC),.pix_x(),.pix_y(),.hsync(),.vsync(),.pix_valid());reg [12:0] current_block = 0;reg [3:0] x_offest = 0;reg [3:0] y_offest = 0;always @ (posedge clk_25mhz)begincurrent_block <= ((myctl.pix_x - (myctl.pix_x % 8)) / 8) + (5 * (myctl.pix_y - (myctl.pix_y % 16))); //乘以80再除以16即乘5x_offest <= 7 - (myctl.pix_x % 8);y_offest <= myctl.pix_y % 16;end

其他几个状态就实例化对应模块就行

    //mode 1VGASim vgasim(.Clk(clk_25mhz),.BTNC(BTNC),.VGA_R(),.VGA_G(),.VGA_B(),.VGA_HS(),.VGA_VS());//mode 2sim sta(.CLk(clk_25mhz),.BTNC(BTNC),.VGA_R(),.VGA_G(),.VGA_B(),.VGA_HS(),.VGA_VS());//mode 3text mytdis(.TCLk(clk_25mhz),.BTNC(BTNC),.VGA_R(),.VGA_G(),.VGA_B(),.VGA_HS(),.VGA_VS());

然后按状态进行选择VGA信息输出

    // ff和00是黑白assign VGA_R = (mode == 0 || mode == 4) ? (asc96[ascii_mem[current_block]-8'h20][y_offest][x_offest] ? 8'hff : 8'h00) : (mode == 1 ? vgasim.VGA_R : (mode == 2 ? sta.VGA_R : mytdis.VGA_R));assign VGA_G = (mode == 0 || mode == 4) ? (asc96[ascii_mem[current_block]-8'h20][y_offest][x_offest] ? 8'hff : 8'h00) : (mode == 1 ? vgasim.VGA_G : (mode == 2 ? sta.VGA_G : mytdis.VGA_G));assign VGA_B = (mode == 0 || mode == 4) ? (asc96[ascii_mem[current_block]-8'h20][y_offest][x_offest] ? 8'hff : 8'h00) : (mode == 1 ? vgasim.VGA_B : (mode == 2 ? sta.VGA_B : mytdis.VGA_B));assign VGA_HS = (mode == 0 || mode == 4) ? myctl.hsync : (mode == 1 ? vgasim.VGA_HS : (mode == 2 ? sta.VGA_HS : mytdis.VGA_HS));assign VGA_VS = (mode == 0 || mode == 4) ? myctl.vsync : (mode == 1 ? vgasim.VGA_VS : (mode == 2 ? sta.VGA_VS : mytdis.VGA_VS));

当时做这个实验的时候感觉十分恶心,因为一路做过来,难度在此突增,而且我使用分布式寄存器堆导致跑比特流慢的夸张,一旦哪里写错了屏幕上一点显示都没有,心态极其容易爆炸。所以当时做完之后就想着要写篇博客帮助后来人,虽然不一定有人读(QAQ)。过完年总算有点时间写了,却又发现忘得差不多了,看着之前写的代码慢慢回忆,写下这篇不是很尽善尽美的文章,希望对你有所帮助

另外如果你选了这门课而且还有退课机会,请快跑。功利地说,前面这些实验只占比60%,最后一个难如登天的大实验占比40%。请不要等到期末月事务繁忙再来后悔谩骂当初的自己。不功利地说,这门课虽然是选修课,但学到的东西和付出的时间并不对等,平时不上课,就嗯自学自己搞实验,我的评价是要么选其他课更好地提高能力,要么直接不选空出时间来享受生活

另外如果你跑不了了,那请你明白有舍有得这个简单的道理。如果最后一个实验实在做不出来了(验收没成果只有及格分),参考本实验,你还有瞒天过海计。算了,到那时你自然会理解我的意思

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/878382.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重叠覆盖解决思路

本文来自博客园,作者:{IceSparks},转载请注明原文链接:https://www.cnblogs.com/IceSparks/p/18697724

4G华为网管常用命令

本文来自博客园,作者:{IceSparks},转载请注明原文链接:https://www.cnblogs.com/IceSparks/p/18697717

5G华为网管常用命令

本文来自博客园,作者:{IceSparks},转载请注明原文链接:https://www.cnblogs.com/IceSparks/p/18697703

阿里云2025年2月免费领取300元无门槛优惠券!

详情免责声明 版权声明 交流群 公众hao服务器有什么用 服务器可以用于管理网络资源,比如控制网络访问、发送/接收电子邮件和 托管网站。服务器用于网站和大型数据库等应用,具有高速计算能力、长期可靠运行、强大的数据吞吐量、高可用性、可靠性、可扩展性和可管理性。24小时不…

win11中本地组策略编辑器(gpedit.msc)打不开解决方案

1,有内容需要用到本地组策略编辑器,结果发现竟然打不开了。后来百度了一下组策略的位置,去找了下果然没有。(下图是解决了问题的截图,没有选中那个文件)2,新建一个TXT,复制以下内容 @echo off pushd “%~dp0” dir /b C:\Windows\servicing\Packages\Microsoft-Windows…

PyCharm接入本地DeepSeek R1实现AI编程

大家好,我是六哥,欢迎来到今天的技术分享!今天我要给大家带来一个超实用的教程,教你如何使用PyCharm接入DeepSeek R1实现AI编程。就算你是编程小白,也能轻松搞定,话不多说,让我们开始吧! 一、为什么要在本地搭建DeepSeek R1模型? 在开始搭建之前,先和大家聊聊这样做的…

LM Studio 0.3.9 无须修改文件,无须魔法,就能下载 hugging face中的模型

LM Studio 无须魔法,无须修改文件,就可以下载 hugging face 中的模型。LM Studio之前版本需要修改配置文件中的下载路径实现从魔塔下载,新版本0.3.9已经可以使用代理下载了。 LM Studio 0.3.9 版本 中的设置中,已经加入 hugging face proxy代码功能,下载速度挺快。设置如下…

2025年这些实用的C#/.NET知识点你都知道吗?

前言 在这个快速发展的技术世界中,时常会有一些重要的知识点、信息或细节被忽略或遗漏。《C#/.NET/.NET Core拾遗补漏》专栏我们将探讨一些可能被忽略或遗漏的重要知识点、信息或细节,以帮助大家更全面地了解这些技术栈的特性和发展方向。✍C#/.NET/.NET Core拾遗补漏合集:h…

ADC模数转换

ADC模数转换1、ADC(Analog Digital Convert)模数转换 引脚上连续变化的模拟电压转换为内存中存储的数字变量 逐次逼近型ADC结构图:ADC0809芯片 2、STM32的ADC框图: ① ADC的输入通道; ② 模拟多路开关;③ 模数转换器;④ 规则组(16个通道+1个数据寄存器)和注入组(4个…

こんばんは

碎碎念——2024.11.18こんばんは~ 没有饭碗可真是麻烦,但遇到命运中的饭碗可真不容易呢?我在这个秋天决定焕然一新,开始替换餐具,但我坚信必须要有那种让我“一见钟情”的碗。在日用品店逛了很久,却始终找不到合适的饭碗。 我曾买过一只很漂亮的抹茶碗,但它太适合茶道了,…

2025多校冲刺省选模拟赛8

2025多校冲刺省选模拟赛8\(T1\) A. 波斯菊 \(10pts/10pts\)每个连通块内以关键点为起点的最长路径 \(d_{i}\) 相互独立,有 \(2n-2k-\sum\limits_{i=1}^{k}d_{k}\) 即为所求。观察到可以直接钦定每个关键点距离其最远的点的是哪个点,这样的话就只需要任意两个路径不相交就能保…