Day8下午
基于上午那个工程,直接修改
首先让DS给跑个 Py 的代码,生成几个数字
这里需要知道 Py 的版本,去 Win+r ,cmd,输入python --version就行
给DS的提示词
我想用 python 生成 10 个中间有 0 到 9 数字的 100x100 像素的 coe 文件,每个像素1bit(二值化),最终coe需要宽度1深度10000,且需要白背景黑字体,并且都放到一个文 件夹里,不要随机生成数字,而是 0 到 9 每个都要,我的 python 版本是3.14.2
import os import numpy as np from PIL import Image, ImageDraw, ImageFont # ==================== 配置 ==================== IMG_SIZE = 100 # 图像尺寸 100x100 DIGITS = range(10) # 要生成的数字 0~9 OUTPUT_DIR = "coe_binary_files" # 输出文件夹 FONT_SIZE = 80 # 字体大小(可根据需要调整) # ============================================= def generate_binary_coe(digit): """ 生成单个数字的 COE 文件内容(1bit 二值化,白色背景,黑色数字) """ # 1. 创建白色背景图像(灰度模式) img = Image.new('L', (IMG_SIZE, IMG_SIZE), color=255) draw = ImageDraw.Draw(img) # 2. 加载字体(优先使用 Arial,若不存在则使用默认字体) try: font = ImageFont.truetype("arial.ttf", FONT_SIZE) except IOError: font = ImageFont.load_default() print(f"警告: 未找到 'arial.ttf',使用默认字体,数字可能偏小。") # 3. 计算文字尺寸并居中绘制(黑色) bbox = draw.textbbox((0, 0), str(digit), font=font) text_w = bbox[2] - bbox[0] text_h = bbox[3] - bbox[1] x = (IMG_SIZE - text_w) // 2 - bbox[0] y = (IMG_SIZE - text_h) // 2 - bbox[1] draw.text((x, y), str(digit), fill=0, font=font) # 黑色 = 0 # 4. 转换为 numpy 数组,并二值化 arr = np.array(img) # 0~255 灰度 binary = (arr > 128).astype(np.uint8) # 白色(>128) -> 1,黑色(<=128) -> 0 # 5. 展平为 10000 个比特 flat = binary.flatten() # shape: (10000,) # 6. 生成 COE 文件内容(radix=2,每位一个数) coe_data = ', '.join(str(b) for b in flat) coe_content = f"memory_initialization_radix=2;\n" coe_content += f"memory_initialization_vector=\n{coe_data};" return coe_content def main(): # 创建输出文件夹 os.makedirs(OUTPUT_DIR, exist_ok=True) # 循环生成每个数字的 COE 文件 for d in DIGITS: content = generate_binary_coe(d) filename = f"digit_{d}.coe" filepath = os.path.join(OUTPUT_DIR, filename) with open(filepath, 'w') as f: f.write(content) print(f"已生成: {filepath}") print(f"\n全部完成!共 {len(DIGITS)} 个 COE 文件保存在 '{OUTPUT_DIR}' 文件夹中。") if __name__ == "__main__": main()
这玩意好像不是图像类型文件,是文本类型但是记事本是完全非人类语言的


这个文件夹放 project_1里面就行
打开 block design
由于要把图放到 BRAM 里面,所以先添加一个 blk(Block Memory Generator)
对他进行配置:
第二栏,由于只需要放单张固定图片,所以用只读的ROM就行,同时因为只要往一个端口输出,所以选择单端口就行

Width是1,深度是100$\times$100像素的,也就说1e4
这个“深度”相当于“栈的深度”,所以相当于每个像素占一位

这边这个路径,如果找对了就不会爆红字了

工程上通常有两种存储
除了DDR的存储
纯PL端的存储一般用到两个,一个是block memory generator,用的是叫 bram 的资源
还有一个是distribute memory generator,是分散式的存储
前者是用固定的东西去存,在芯片上有一种固定的资源
后者用的是查找表和触发器做出来的,比较占用资源

刚才这里,BRAM Controller模式是方便跟 hi 总线连接的
“HI总线”通常指代以AXI协议为代表的高性能总线,其连接过程在Vivado这类开发工具中很直观
Stand Alone没法连接
如果需要跟 PS 交互的话,就选上面那个模式,会更适配相应的组件
而我们的目的是在 PL 端显示图片,不需要与 PS 端交互,就用的下面这个模式

这里Use ENA Pin,如果我给他一个enable信号(ena),他才会读取相应的信号
上面那个是一直读取信号的
后面是惊险刺激的复制粘贴环节,0~9每个文件都选上

这边对于这个控制 vga的模块,我们想要的是他不止控制 vga,同时控制右上角的框
、
这边打开 source 里面对应的 vga_top,让 AI 改写这个代码,实现上述功能
module vga_top ( input wire vga_valid, input wire [15:0] data_in, output wire [15:0] data_out ); assign data_out = vga_valid ? data_in : 16'h0000; endmodule 请用 Verilog 改写这个视频字符叠加模块 vga_data,功能是在 VGA 视频流右上角叠加一个100×100 像素的数字/图案,行是水平从 923 记到到 1022,列是 0 到 99。 模块接口: 输入: sclk(像素时钟)、s_rst_n(低电平复位)、vga_vsync(场同步)、vga_hsync(行同步)、 (vga_hsync 和 vga_vsync 为高有效)active_video(显示有效区使能)ROM 接口:rom_sel[3:0] (选择显示哪个数字,0~9)、rd_addr[13:0(] 输出给 ROM 的读地址)、rom0_data ~ rom9_data (10 个 ROM 的数据输入,各 1 bit) 视频:rgb_data_i[15:0](原始 RGB565 输入)、 rgb_data_o[15:0](叠加后的 RGB565 输出) 功能要求: 根据 vga_hsync 和 vga_vsync 生成 行列计数器(行计数到 1023 自动换行,最大 720 行) 用 rom_sel 选择当前显示的字符 (0~9) 根据对应 ROM 数据输出全白 16'hFFFF 或全黑 16'h0000 同时产生 ROM 读地 址 rd_addr,在叠加区域内逐像素递增 叠加区域外且 active_video 有效时,直通原始视频数 据 非有效显示区输出纯黑 16'h0000 注意: rom_sel 是异步输入,需要用两级寄存器同步 到像素时钟域。
module vga_data ( // 时钟与复位 input wire sclk, // 像素时钟 input wire s_rst_n, // 低电平复位 // VGA 同步信号(高有效) input wire vga_vsync, // 场同步 input wire vga_hsync, // 行同步 input wire active_video, // 显示有效区 // ROM 选择(异步输入,需同步) input wire [3:0] rom_sel, output reg [13:0] rd_addr, // ROM 读地址 // 10 个 ROM 数据位(各 1 bit) input wire rom0_data, input wire rom1_data, input wire rom2_data, input wire rom3_data, input wire rom4_data, input wire rom5_data, input wire rom6_data, input wire rom7_data, input wire rom8_data, input wire rom9_data, // 视频数据 input wire [15:0] rgb_data_i, output reg [15:0] rgb_data_o ); // ============================================================ // 1. 异步输入 rom_sel 两级同步 // ============================================================ reg [3:0] rom_sel_d1, rom_sel_d2; always @(posedge sclk or negedge s_rst_n) begin if (!s_rst_n) begin rom_sel_d1 <= 4'd0; rom_sel_d2 <= 4'd0; end else begin rom_sel_d1 <= rom_sel; rom_sel_d2 <= rom_sel_d1; end end wire [3:0] rom_sel_sync = rom_sel_d2; // ============================================================ // 2. 行列计数器(水平 0~1023,垂直 0~719) // ============================================================ reg [9:0] h_cnt; // 水平位置(列) reg [9:0] v_cnt; // 垂直位置(行) always @(posedge sclk or negedge s_rst_n) begin if (!s_rst_n) begin h_cnt <= 10'd0; v_cnt <= 10'd0; end else if (vga_vsync) begin // 场同步有效时,复位行列计数器(新一帧开始) h_cnt <= 10'd0; v_cnt <= 10'd0; end else if (active_video) begin if (h_cnt == 10'd1023) begin h_cnt <= 10'd0; if (v_cnt == 10'd719) v_cnt <= 10'd0; else v_cnt <= v_cnt + 1'b1; end else begin h_cnt <= h_cnt + 1'b1; end end end // ============================================================ // 3. 叠加窗口判定(右上角:水平 923~1022,垂直 0~99) // ============================================================ wire in_overlay_region; assign in_overlay_region = (v_cnt < 10'd100) && (h_cnt >= 10'd923) && (h_cnt <= 10'd1022); // ============================================================ // 4. ROM 读地址生成(窗口内逐像素递增) // ============================================================ always @(posedge sclk or negedge s_rst_n) begin if (!s_rst_n) begin rd_addr <= 14'd0; end else if (vga_vsync) begin rd_addr <= 14'd0; // 帧开始复位地址 end else if (v_cnt == 10'd0 && h_cnt == 10'd923 && active_video) begin rd_addr <= 14'd0; // 窗口左上角复位地址 end else if (in_overlay_region && active_video) begin rd_addr <= rd_addr + 1'b1; // 窗口内逐像素递增 end end // ============================================================ // 5. 根据同步后的 rom_sel 选择对应的 ROM 数据位 // ============================================================ wire selected_rom_bit; assign selected_rom_bit = (rom_sel_sync == 4'd0) ? rom0_data : (rom_sel_sync == 4'd1) ? rom1_data : (rom_sel_sync == 4'd2) ? rom2_data : (rom_sel_sync == 4'd3) ? rom3_data : (rom_sel_sync == 4'd4) ? rom4_data : (rom_sel_sync == 4'd5) ? rom5_data : (rom_sel_sync == 4'd6) ? rom6_data : (rom_sel_sync == 4'd7) ? rom7_data : (rom_sel_sync == 4'd8) ? rom8_data : rom9_data; // ============================================================ // 6. 最终视频输出(寄存器输出,延迟一拍) // ============================================================ always @(posedge sclk or negedge s_rst_n) begin if (!s_rst_n) begin rgb_data_o <= 16'h0000; end else if (!active_video) begin rgb_data_o <= 16'h0000; // 非有效区输出纯黑 end else if (in_overlay_region) begin // 叠加区域:ROM 1 → 全白,ROM 0 → 全黑 rgb_data_o <= selected_rom_bit ? 16'hFFFF : 16'h0000; end else begin rgb_data_o <= rgb_data_i; // 窗口外直通原始视频 end end endmodule
改完之后点一手这里

有警告是正常的,因为有些端口没连上
啧我这边第一遍不太成功,block design 的模块依旧是之前的形式,可能是他没正常更新?总之我把 vga_top 删了,补充了个 vga_tidai 进去,放的还是这个新代码
sclk 连的像素时钟,第一个 out ,就是那个74.25兆的
reset(rst)正常连
这几个端口也就自然跟out连上了

右边是这样

然后对于0~9每个Block Memory Generator,都是这样
可见这个 vga_data 模块左边还漏个 rom_sel 端口,抓一个 vio 模块去控制,然后再用 PS 端控制
加个 vio 模块,如下配置

这里调成跟 rom_sel 相同的四位

他的 clk 也是像素时钟

成品如下:

9‘42有一个小细节

浙公网安备 33010602011771号