0%

【EE#0x00】Cameudisの数逻探索笔记

这学期的数字逻辑与部件设计,误打误撞地选了荣誉课(表面是不是,但是孙晓光老师是当作荣誉课上的哈哈哈),意外地发现非常非常有趣!!
并且似乎和我大一上的梦想契合了——那时候我想做一个像志辉君一样的全栈工程师。(现在想做大黑阔)
总之,我决定好好享受这门课!理论课的笔记我都统一记在IPAD上;另外准备写一些实验的记录或者笔记在电脑/博客上(课内的,以及我自己的整活,如果之后我还有空来整活)。

第0期是关于开发流程和板子I/O的笔记整理。

基础 - 整体开发流程

开发板

首先,需要一个开发用的板子(废话)。我们老师发的是Xilinx Artix-7 100t,有非常豪华的配置,甚至比微电子发的板子还要豪华,不过我们是一组发一个(虽然我组拿了两个)(希望别被助教和老师看到),微电子是一人发一个。

嘿嘿嘿,Artix-7

开发平台

其次,需要一个开发平台,也就是EDA。我们课程使用的是Vivado2019.2,Xilinx公司FPGA的集成开发软件。
然而在安装的时候我遇到了非常痛苦的问题,不论我怎么整,都没办法在我的电脑上面安装Vivado2019.2,且基本能试的方法都以及试过了……

那么答案只有一个了!那就是装在虚拟机上!!
所以我部署了一台Ubuntu16.04虚拟机,然后把Vivado成功地安装了上去!
注意:在Linux下安装Vivado,在安装时不会帮你安装驱动(Cable Driver),需要用户在安装完Vivado之后自行安装。教程参见Installing Cable Drivers • Vivado Design Suite User Guide: Release Notes, Installation, and Licensing (UG973)

开发流程

在板子上开发,比开发C语言应用程序要复杂得多。
使用C语言进行开发,真正“优化代码”的只有一步,就是编译器从源代码生成汇编代码的那一步。
然而使用硬件描述语言进行开发,优化的似乎有好几步。程序员写的代码,需要经过很多步,才会变成真正的硬件实现。用软件实现硬件,显然比软件实现软件要困难,所以流程的复杂性也是可以理解的。

进入正题,开发的流程如下:

  1. 创建/修改项目文件/代码文件
  2. 仿真(Simulation) - 编写仿真文件,对代码描述的功能进行验证
  3. 综合(Synthesis) - 将设计输入翻译为由基本逻辑元件(如逻辑门、RAM、触发器等)组成的逻辑连接(网表)
  4. 实现(Implementation) - 根据所选芯片的型号,将网表适配到芯片上
  5. 烧录(Program and Debug) - 将.bit文件烧录到连接的芯片上

在开发流程中,需要我们来编写的文件有:

  1. 代码文件 - 描述想要开发的硬件功能
  2. 仿真文件 - 实例化代码,并给其输入,用于对代码进行仿真验证
  3. 约束文件 - 将板子上的实际接口记录为变量

其中,约束文件不用自己编写,板子的开发商会提供一个完整的约束文件,里面记录了板子上所有的接口。开发者只需要启用需要用到的接口,把不需要的接口全都注释掉就可以。

开发语言

硬件开发使用硬件描述语言HDL,也即Hardware Description Language。
课程中使用和Verilog差不多的SystemVerilog。

Verilog的语法和C非常像,但是有着根本的区别:

  • C语言程序是串行执行的
  • Verilog程序是并行执行的

对于运行于电脑上的程序来说,一切指令都是顺序执行的,CPU根据IP取指令出来执行,执行完了再取下一条出来执行……若没有多核,计算机上似乎不存在真正的并行(多件事情同时执行),而只有伪并行,也就是并发(多件事情在同一时间段内发生),通过操作系统的上下文切换来完成。

然而,在硬件层面是真正存在并行的!一个信号可以从一个输入同时到达两个输出,两个信号可以同时输出到一个门的输入中……
这给刚接触到上下文切换带来的Race(竞争)的我带来了极大的喜悦(不是)。

回到Verilog的正题。Verilog作为一个工具,并不需要我们去执着于具体的语法(just like linux shell)

胡伟武(龙芯之父,计算所总工程师)说过:学会了以下3个语句,对所有的设计都够用了。

  • assign 语句:用于描述组合逻辑;
  • always(@poedge clock) 语句:用于描述时序逻辑;
  • 模块调用 语句。

所以我这里也不详述具体的语法规则了。想用什么网上一搜就行(比如搜索“verilog位混合”)。
有一个Verilog的练习网站Problem sets - HDLBits,闲着没事可以玩玩(我现在忙得没空玩了悲)。

基础 - 板子的基本I/O

I/O,Input/Output,也就是我们与板子的交互。
我们能在板子上整的所有活,都受到I/O的限制。但换一个角度,正是I/O给了我们整活的可能性。

所以我们来看看板子上有哪些目前知识水平(勉强)可以使用的基本I/O。
Artix-7 基本I/O电路图

LED, Switch, Button

这三个部件是最简单直观的。

LED,也就是发光二极管。在Artix-7中,LED的输出端统一接地,因此只要给一个高电平,LED就会亮起,反之则会关闭。十分的简单。

Switch,也就是拨码开关,约束文件里一般记为SW。把开关往上一拨,就会给一个高电平输入;往下一拨,就是低电平。十分的简单。

Button,也就是按钮,约束文件里一般根据方向来命名各个开关,如BTNC表示正中间的按钮,U、D分别表示Up、Down,L、R分别表示Left、Right。
按下按钮,就会给一个高电平;松开就是低电平。十分的简单。

Tri-Color LED

板子上有两个三色的LED灯,每个灯由三个有色LED组成,分别是R、G、B。因此一个三色LED的所有状态数是 $2^3=8$ 种。
对于每个有色LED,驱动方式和普通LED一样,给高电平就亮,低电平就暗。

七段数码管

以上讲的所有Output都是 共阴极 的,也就是说它们都一端接地,给一个高电平就会工作。
然而,七段数码管不一样,它是 共阳极 的,也就是说它们都一端接一个恒定的高电平。点亮七段数码管,需要给一个低电平,才能形成电势差,形成电流。若给一个高电平,则器件两端电压相同,没有电流,就停止工作。

并且,七段数码管作为一个非常神奇的期间,包含了一个非常重要的思想。

让我们先暂停对于数电的探索,来看看我们的显示屏。(也就是你现在正看着的东西)请问读者老板,你的显示屏分辨率是多少?1080p?还是2K?甚至是强大的4K屏?
本人的笔记本是2K的啦~
那么我们来想想一个问题:显卡是负责生成图像的器件,那么显卡生成完需要显示的图像之后,这个图像如何连到显示屏上显示出来?

一种自然的想法是每个像素点都连一根电线到显卡,就像板子上的LED一样,但你想想就会觉得不对劲——难道一根hdmi线(或者vga线,随便你)里塞得下这么多线吗?
原来,工程师们为了应对这个问题,想出了一种策略,我想将其称为电线复用
(岔出去一下:在CSAPP的Datalab中,我们需要缩减实现特定功能所用到的操作符个数,也就是简化,所用到的最重要的思想也就是复用的思想。)

在七段数码管中,只有8+7+1个引脚:

  • 8个AN负责开关8个七段数码管;
  • 7个A2G负责控制所有七段数码管的显示内容;
  • 1个DP负责控制所有小数点。
    注意:这16个引脚全都是以低电平标识点亮,以高电平标识熄灭。

这种情况下,想要玩谁笑到最后是比较简单的啦,只要打开八个AN,然后A2G全都打开,你就在开发板上有了一整排的8,横过来看就是8个并排的西瓜投手(认真)。

但我们显然不想让所有的七段数码管都显示同样的数字,毕竟这浪费了整整7个7端数码管的信息量。
所以我们需要复用

我们知道,人眼是有视觉暂留的,也就是上帝在创造我们的世界时,留下的刷新率上限(具体来说,通常为24fps)。
所以,如果我们用一个飞快的速度,每次只打开一个七段数码管,但疯狂切换打开的数码管。这时候,人眼来不及捕捉变化(我甚至有点不确定LED能不能反应过来),就会认为所有的七段数码管都是同时开启的
(这和操作系统并发运行多个进程是完全同理的,都是利用其极快的切换速度,做到表面上的并行。)

在疯狂切换数码管电源开关的基础上,再加上同频率地切换A2G和DP的值,就可以做到给每个七段数码管分配特定的显示内容

以上就是分时显示的大致原理,具体实现需要用到很多现在还没学的时序电路知识,因此这里先不细究(对应的Lab2就是在给的分时显示实例代码基础上修改,因此难度上还是比较友好滴)。