FPGA纯verilog手写HDMI发送IP 提供源码和技术支持

news2024/11/15 13:25:10

目录

  • 1、前言
  • 2、设计思路和框架
    • TMDS 编码算法
    • OSERDESE串并转换
  • 3、顶层源码和IP封装
  • 4、源码和IP获取

1、前言

本设计使用Xilinx原语和自己手写的代码实现了HDMI发送功能,纯verilog手写,有源码,也提供封装好的IP,你喜欢用例化的方式就用源码,你喜欢搭建BD工程就用IP,目前IP的适应器件为zynq,如果是用7系列FPGA的兄弟可以改一下芯片型号即可使用。
这个HDMI发送模块我已在项目中多次使用,稳定性和实用性可以保证,最大支持1080P@60Hz帧率。
本文详细描述了设计方案,工程代码编译通过后上板调试验证,可直接项目移植,适用于在校学生、研究生项目开发,也适用于在职工程师做项目开发,可应用于医疗、军工等行业的数字成像和图像传输领域;
提供完整的、跑通的工程源码和技术支持;
工程源码和技术支持的获取方式放在了文章末尾,请耐心看到最后;
关于HDMI的理论介绍这里不多说,请自行搜索,很简单,本文只讲如何代码层级实现的干货。

2、设计思路和框架

HDMI编码框图如下:
在这里插入图片描述Encoder 模块负责对数据进行编码,Serializer 模块对编码后的数据进行并串转换,最后
通过 OBUFDS 转化成 TMDS 差分信号传输。
整个系统需要两个输入时钟,一个是视频的像素时钟 Pixel Clk,另外一个时钟 Pixel Clk x5 的频率是像
素时钟的五倍。由前面的简介部分我们知道,并串转换过程的实现的是 10:1 的转换率,理论上转换器需要一个 10 倍像素时钟频率的串行时钟。这里我们只用了一个 5 倍的时钟频率,这是因为 OSERDESE2 模块可以实现 DDR 的功能,即它在五倍时钟频率的基础上又实现了双倍数据速率。
TMDS 连接的时钟通道我们采用与数据通道相同的并转串逻辑来实现。通过对 10 位二进制序列10’b11111_00000 在 10 倍像素时钟频率下进行并串转换,就可以得到像素时钟频率下的 TMDS 参考时钟。
另外需要注意的是,图中左下脚 HDMI 的音频/附加数据输入在本次实验中并未用到,因此以虚线表示。

TMDS 编码算法

Encoder 模块完成TMDS 编码算法,dvi_encoder 模块按照 DVI 接口规范中 TMDS 编码算法对输入的 8 位像素数据以及 2 位行场同步信号进行编码。该模块是 Xilinx 应用笔记 XAPP460 中所提供的编码模块,其具体实现的编码算法如下图所示:在这里插入图片描述TMDS 通过逻辑算法将 8 位字符数据通过最小转换编码为 10 位字符数据,前 8 位数据由原始信号经运
算后获得,第 9 位表示运算的方式,1 表示异或 0 表示异或非。经过 DC 平衡后(第 10 位),采用差分信号传输数据。第 10 位实际是一个反转标志位,1 表示进行了反转而 0 表示没有反转,从而达到 DC 平衡。
接收端在收到信号后,再进行相反的运算。TMDS 和 LVDS、TTL 相比有较好的电磁兼容性能。这种算
法可以减小传输信号过程的上冲和下冲,而 DC 平衡使信号对传输线的电磁干扰减少,可以用低成本的专用电缆实现长距离、高质量的数字信号传输。
算法中各个参数的含义如下图所示:
在这里插入图片描述Encoder 模块顶层接口如下:

module dvi_encoder (
  input            clkin,    // pixel clock input
  input            rstin,    // async. reset input (active high)
  input      [7:0] din  ,    // data inputs: expect registered
  input            c0   ,    // c0 input
  input            c1   ,    // c1 input
  input            de   ,    // de input
  output reg [9:0] dout      // data outputs
);

代码部分并不复杂,就是将TMDS 编码算法的流程图翻译成了 verilog语言。

OSERDESE串并转换

TMDS 编码之后的数据由 Serializer 模块进行并串转换,通过调用 OSERDESE2 原语来实现 10:1 的并串转换。原语是 Xilinx 器件底层硬件中的功能模块,它使用专用的资源来实现一系列的功能。相比于 IP 核,原语的调用方法更简单,但是一般只用于实现一些简单的功能。
需要注意的是,一个 OSERDESE2 只能实现最多 8:1 的转换率,在这里我们通过位宽扩展实现了 10:1
的并串转换,如下图所示:
在这里插入图片描述OSERDESE2 位宽扩展通过两个 OSERDESE2 模块来实现,其中一个作为 Master,
另一个作为 Slave,通过这种方式最多可实现 14:1 的并串转换。需要注意的是,在位宽扩展时,Slave 模块的数据输入端只能使用 D3 至 D8。
OSERDESE串并转换模块顶层接口如下:

module serializer_10_to_1(
    input              reset,              
    input              paralell_clk,       
    input              serial_clk_5x,    
    input   [9:0]   paralell_data,     
    output 		 serial_data_out    
    );

3、顶层源码和IP封装

将各个功能模块进行例化,即完成了本设计,为了适应不同的使用需求,提供源码的同时也将源码进行了IP封装,两者一并提供。
顶层源码如下:

module helai_hdmi_out(
	input         clk_hdmi     ,
	input         clk_hdmix5   ,
	input         reset_n      ,
	input         i_vga_hs     ,
	input         i_vga_vs     ,
	input         i_vga_de     ,	
    input  [23:0] i_vga_rgb    ,   
    output        o_hdmi_clk_p ,
    output        o_hdmi_clk_n ,
    output [2: 0] o_hdmi_data_p,
	output [2: 0] o_hdmi_data_n            
);
   
wire        reset;
    
//²¢ÐÐÊý¾Ý
wire [9:0]  red_10bit;
wire [9:0]  green_10bit;
wire [9:0]  blue_10bit;
wire [9:0]  clk_10bit;  
  
//´®ÐÐÊý¾Ý
wire [2:0]  tmds_data_serial;
wire        tmds_clk_serial;

//*****************************************************
//**                    main code
//*****************************************************   
assign clk_10bit = 10'b1111100000;

//Òì²½¸´Î»£¬Í¬²½ÊÍ·Å
asyn_rst_syn reset_syn(
    .reset_n    (reset_n),
    .clk        (clk_hdmi),
    .syn_reset  (reset)    //¸ßÓÐЧ
    );
  
//¶ÔÈý¸öÑÕɫͨµÀ½øÐбàÂë
dvi_encoder encoder_b (
    .clkin      (clk_hdmi),
    .rstin	    (reset),
    
    .din        (i_vga_rgb[7:0]),
    .c0			(i_vga_hs),
    .c1			(i_vga_vs),
    .de			(i_vga_de),
    .dout		(blue_10bit)
    ) ;

dvi_encoder encoder_g (
    .clkin      (clk_hdmi),
    .rstin	    (reset),
    
    .din		(i_vga_rgb[15:8]),
    .c0			(i_vga_hs),
    .c1			(i_vga_vs),
    .de			(i_vga_de),
    .dout		(green_10bit)
    ) ;
    
dvi_encoder encoder_r (
    .clkin      (clk_hdmi),
    .rstin	    (reset),
    
    .din		(i_vga_rgb[23:16]),
    .c0			(i_vga_hs),
    .c1			(i_vga_vs),
    .de			(i_vga_de),
    .dout		(red_10bit)
    ) ;
    
//¶Ô±àÂëºóµÄÊý¾Ý½øÐв¢´®×ª»»
serializer_10_to_1 serializer_b(
    .reset              (reset),                // ¸´Î»,¸ßÓÐЧ
    .paralell_clk       (clk_hdmi),                 // ÊäÈë²¢ÐÐÊý¾ÝʱÖÓ
    .serial_clk_5x      (clk_hdmix5),              // ÊäÈë´®ÐÐÊý¾ÝʱÖÓ
    .paralell_data      (blue_10bit),           // ÊäÈë²¢ÐÐÊý¾Ý
//    .paralell_data      (10'h146),           // ÊäÈë²¢ÐÐÊý¾Ý

    .serial_data_out    (tmds_data_serial[0])   // Êä³ö´®ÐÐÊý¾Ý
    );    
    
serializer_10_to_1 serializer_g(
    .reset              (reset),
    .paralell_clk       (clk_hdmi),
    .serial_clk_5x      (clk_hdmix5),
    .paralell_data      (green_10bit),
 //     .paralell_data      (10'h146),   

    .serial_data_out    (tmds_data_serial[1])
    );
    
serializer_10_to_1 serializer_r(
    .reset              (reset),
    .paralell_clk       (clk_hdmi),
    .serial_clk_5x      (clk_hdmix5),
    .paralell_data      (red_10bit),
 //    .paralell_data      (10'h146),    

    .serial_data_out    (tmds_data_serial[2])
    );
            
serializer_10_to_1 serializer_clk(
    .reset              (reset),
    .paralell_clk       (clk_hdmi),
    .serial_clk_5x      (clk_hdmix5),
    .paralell_data      (clk_10bit),

    .serial_data_out    (tmds_clk_serial)
    );
    
//ת»»²î·ÖÐźŠ 
OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/Oµçƽ±ê׼ΪTMDS
) TMDS0 (
    .I                  (tmds_data_serial[0]),
    .O                  (o_hdmi_data_p[0]),
    .OB                 (o_hdmi_data_n[0]) 
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/Oµçƽ±ê׼ΪTMDS
) TMDS1 (
    .I                  (tmds_data_serial[1]),
    .O                  (o_hdmi_data_p[1]),
    .OB                 (o_hdmi_data_n[1]) 
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/Oµçƽ±ê׼ΪTMDS
) TMDS2 (
    .I                  (tmds_data_serial[2]), 
    .O                  (o_hdmi_data_p[2]), 
    .OB                 (o_hdmi_data_n[2])  
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/Oµçƽ±ê׼ΪTMDS
) TMDS3 (
    .I                  (tmds_clk_serial), 
    .O                  (o_hdmi_clk_p),
    .OB                 (o_hdmi_clk_n) 
);
  
endmodule

封装后的IP如下:
在这里插入图片描述

4、源码和IP获取

福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
资料获取方式:点击获取
网盘资料如下:
在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WebRTC 系列之视频辅流

WebRTC 中的 SDP 支持两种方案: PlanB 方案 和 Unified Plan 方案。早期我们使用多PeerConnection的 Plan B 方案中只支持一条视频流发送,这条视频流,我们称之为”主流”。目前我们使用单 PeerConnection 的 Unified Plan 方案,新…

二叉树——把二叉搜索树转换为累加树

538. 把二叉搜索树转换为累加树 链接 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下&#xf…

春天到了,来一场 VoxEdit 创作大赛吧!

春天的气息扑面而来,这是让你尽情绽放创造力的最佳时机!我们将以「春天」为主题来一场 VoxEdit 大赛。在这里,你可以展示你的才华并赢得 $SAND 奖励! 无论你是专业的设计师,还是仅仅喜欢创造美丽的艺术,这场…

有趣的阻抗变换

阻抗变换在很多人看来很神秘,甚至不可理喻: “什么是匹配网络?” “为什么要在负载电路之前加这么多电感电容?” “如果负载是100欧姆要与源阻抗50欧匹配,直接在负载并联一个100欧负载不就行了吗”……这样的问题常…

项目管理软件中日历的作用

为什么在项目管理软件中使用日历?日历是跟踪即将举行的会议、截止日期和里程碑的有用工具。它们可以帮助您可视化您的日程安排并提醒您重要事件,例如假期和休假时间。 虽然人们经常有各种各样的日历工具可供选择,包括从办公室墙上的纸质日历…

Stochastic Approximation 随机近似方法的详解之(二)Robbins-Monro Algorithm

6.2 Robbins-Monro Algorithm RM算法是随机近似领域的先驱性工作。众所周知的随机梯度下降算法是RM算法的一种特殊情况。后面我们再介绍具体的细节。 先看一个例子: 我们想要去求下面这个等式的根, BTW,很多问题可以被转化为求根问题。比…

嵌入式学习笔记——基于Cortex-M的单片机介绍

基于Cortex-M的单片机介绍前言生产厂商及其产品线ARM单片机的产品线命名规则留个作业习单片机的资料准备STM32开发所需手册1.芯片的数据手册作业2前言 本文继续接着上一篇中关于Cortex-M的介绍,来记录一些关于ARM系单片机的知识。 生产厂商及其产品线 芯片厂商在…

论坛性能测试难点有哪些?

1 测试工具方面 用户和业务模型分析搭建合适的脚本开发(不根据用户和业务的模型来开发脚本,认为要回归成功即可)合适的需求分析转化为场景设计(不知道如何根据需求进行场景设计)大容量系统的数据生成和使用大型系统的…

金蝶国产化中间件和人大金仓数据库

金蝶Apusic分布式消息队列不需要配置用户名密码rabbitmq:enable: truehost: 192.168.1.233port: 5672<!-- Spring Boot RabbitMQ 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</ar…

webrtc音频系列——4、RTP与RTCP协议

如果让你从0开发一套实时互动直播系统&#xff0c;你首先要选择网络传输协议。UDP 还是 TCP&#xff1f;答案是&#xff1a;UDP。为什么实时传输不能用 TCP &#xff1f;TCP 的目的就是实现数据的可靠传输&#xff0c;因此他有一套 握手&#xff0c;发送 -> 确认&#xff0c…

linux下安装jenkins

1.初始化Jenkins安装环境 系统版本&#xff1a;Red Hat Enterprise Linux 8.7 将脚本文件jenkins_install_env.sh 、 jenkins_install.sh和apache-maven-3.6.2-bin.tar.gz、jdk-8u251-linux-x64.tar.gz都上传到/usr/local/src目录下执行jenkins_install_env.sh脚本初始化Jenki…

嵌入式开发工具箱【持续更新中】【VMware、Ubuntutftp、nfs、SecureCRT、XShell、Source Insight 4.0】

一、概述 本文主要介绍嵌入式开发过程中需要用到的工具及简单的使用方法。避免在搭建嵌入式开发环境时&#xff0c;需要四处寻找文档&#xff0c;收藏此文章&#xff0c;一文搞定。 大多数嵌入式开发环境是使用Linux作为目标开发系统&#xff0c;所以开发主机一般都是Linux系统…

若依学习(前后端分离版)——启动时发生了啥?(@PostConstruct)(mybatis log free)

我们可以发现若依启动时执行了一些sql我们可以安装一个插件mybatis log free 来更好的进行sql查看 &#xff0c;安装后需要修改一下若依的日志配置如下查看日志&#xff0c;我们发现执行了三个方法&#xff08;&#xff09;&#xff0c;分别查询了一些数据。以第二个方法为例子…

python 如何存储数据 (python 的文件和异常)

文章目录存储数据1. 使用 json.dump() 和 json.load()json.dump()2. 保存和读取用户生成的数据存储数据 很多程序都要求用户输入某种信息&#xff0c;如让用户存储游戏首选项或提供要可视化的数据。不管专注的是什么&#xff0c;程序都把用户提供的信息存储在列表和字典等数据结…

这些个 axios 工具函数,你都掌握了吗

前言 周末过得真快&#xff0c;看个源码时间就过去了&#x1f440;。 在上周看做项目的时候看到了项目里封装的 axios&#xff0c;对其封装的原理没有弄清楚&#xff0c;于是周末的时候便抽了点空闲时间来看了看 axios 的源码&#xff0c;将其研究研究。 源码阅读 这里就不…

实战:一天开发一款内置游戏直播的国产版Discord应用【附源码】

游戏直播是Discord产品的核心功能之一&#xff0c;本教程教大家如何1天内开发一款内置游戏直播的国产版Discord应用&#xff0c;用户不仅可以通过IM聊天&#xff0c;也可以进行语聊&#xff0c;看游戏直播&#xff0c;甚至自己进行游戏直播&#xff0c;无任何实时音视频底层技术…

数据增广真有那么神奇吗?

作者&#xff1a;皮皮雷 来源&#xff1a;投稿 编辑&#xff1a;学姐 论文题目 How Effective is Task-Agnostic Data Augmentation for Pretrained Transformers? 论文作者 S. Longpre, Y. Wang, and C. DuBois 论文发表于 2020 EMNLP findings 摘要 任务无关的数据增广…

chatgpt功能展示

Chatgpt 不知道大家刷抖音的时候是否刷到了人工智能要取代人类的一些短视频&#xff0c;他们所提到的主角就是一个AGI模型——chatgpt&#xff0c;资本对其的追求程度可以用火爆&#x1f525;来形容了吧&#xff0c;先给大家讲一下chatgpt吧&#xff0c;如果没兴趣看我扯犊子可…

网络安全入门学习:社会工程学

在电影《我是谁&#xff1a;没有绝对安全的系统》中&#xff0c;主角本杰明充分利用自己高超的黑客技术&#xff0c;非法入侵国际安全系统&#xff0c;并在最后逃之夭夭。在电影中&#xff0c;有一句经典的台词&#xff1a; 所有黑客手段中最有效的、最伟大的幻想艺术——社会…

windows10安装ubantu双系统

windows10安装ubantu双系统 文章目录windows10安装ubantu双系统一、安装前准备1.前期说明2.制作U盘启动器3.设置硬盘分区相关4.设置给ubantu系统的硬盘大小&#xff0c;设置为未分配&#xff08;删除卷&#xff09;二、进行安装1.设置bios相关2.进入bios启动界面选择U盘安装3.进…