版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址

  http://www.cnblogs.com/Colin-Cai/p/12242650.html 

  作者:窗户

  QQ/微信:6679072

  E-mail:6679072@qq.com

  上一章介绍了数字电路的重要概念原语,可以用来做门级的元件。这一章里,我们在原语的基础上再引入模块的概念。

  

  Verilog模块

  

  模块就是电路的具体描述了,当然上一章的原语也是用来描述电路,但一般原语是为了构造门级或者不可分割的元件级电路,而模块则是包含更广的需求,拿来设计更为复杂的电路。比如我们可以用Verilog模块来描述一段4位加法器。

module add4(in1, in2, cin, out);
input [:]in1, in2;
input cin;
output [:]out;
assign out = in1 + in2 + cin;
endmodule

  以上是RTL(Register Transfer Level),不能直接反映电路的形状(虽然在不优化的情况下与粗粒度上的电路存在对应关系),如果用门级电路来描述就比较多,不过我们门级描述其实可以引入分层设计。以下体现分层设计的思想。

  先设计一个半加器(Half adder),也就是两个bits(姑且称为a、b)的输入,把两者看成1位二进制数,求和得到一个2位二进制输出(称低位为s,高位称为c)。于是很容易得到,用一个异或门得到低位输出s,用一个或门得到高位输出c。Verilog描述如下:

module half_add(a, b, s, c);
input a, b;
output s, c;
xor u1(s, a, b);
and u2(c, a, b);
endmodule

  于是用两个半加器和一个或门级联就得到了一位的全加器,这应该是在学习数字电路的时候我们都会很熟悉的结果:

  用Verilog描述:

module full_add(a, b, cin, out);
input a, b, cin;
output [:]out; half_add u1(
.a(a),
.b(b),
.s(s1),
.c(s2)
);
half_add u2(
.a(s1),
.b(cin),
.s(out[]),
.c(s3)
);
or u3(out[], s2, s3);
endmodule

  最终,4个全加器级联成1个4位加法器:

module add4(in1, in2, cin, out);
input [:]in1, in2;
input cin;
output [:]out; wire c0, c1, c2; full_add u1 (
.a(in1[]),
.b(in2[]),
.cin(cin),
.out({c0, out[]})
);
full_add u2 (
.a(in1[]),
.b(in2[]),
.cin(c0),
.out({c1, out[]})
);
full_add u1 (
.a(in1[]),
.b(in2[]),
.cin(c1),
.out({c2, out[]})
);
full_add u1 (
.a(in1[]),
.b(in2[]),
.cin(c2),
.out(out[:])
);
endmodule

  我们在设计数字电路的时候,无论是用原始的原理图设计,还是使用HDL设计,一个大一点的设计一般都是如此级联或分层,某些时候可以借助软件的设计思想,比如可以提取公共的公共的功能,单独设计模块,然后在不同的地方例化。Verilog甚至有parameter这样的东西,使得相同的设计在不同的例化中成为不同位数的电路。

  

  很多结构化的模型里都会有图(graph)的概念,比如在流计算、神经网络,地图、网络中对于路由的计算等。

  

  

  比如上面这个电路,一共存在a、b、c、d、e、f、g七个在门之间传递信号的连接线,连接了一个非门、一个或门、一个异或门和一个与门。

  我们把这些门看成是图的点,而把两个点之间的连接看成是一个有向边,也就是一个连接线可能不止对应一条边,这样电路图就是一个有向图了。可是我们很快发现a、b、c、d只有一个点可以连,无法构成边,这显然不符合图论。但同时,我们意识到a、b、c、d正好是整个电路对外的输入/输出信号。于是为了图的完整,我们再为每个输入/输出造特殊的顶点类型,这类顶点只与具体输入/输出信号连接。这样,图就完整了。实际上,大多数EDA引入原理图输入的时候都会引入这样的一个标记,以下是QuatusII画的原理图

  于是我们得到之前要表示的电路的图中所有的边与顶点如下:

顶点:

  A : input-pin(a)

  B : input-pin(b)

  C : input-pin(c)

  D : output-pin(d)

  E : not-gate([a],[e])

  F : or-gate([b,c],[f])

  G : xor-gate([e,f],[g])

  H : and-gate([e,g],[d])

  边:

  A->E

  B->F

  C->F

  E->H

  E->G

  F->G

  G->H

  H->D  

  结合上一节所说,再次确定一下,一个模块所表示的图的顶点可能是input/output、原语或者别的模块。

  我们知道,时序电路里的基本元件,比如各种锁存器、触发器,是用各种组合电路反馈得到的。反馈对应于有向图有环。实际上,很多HDL是支持反馈的,比如verilog,完全可以成功仿真。但反馈是要靠不同的手段才可以推出其逻辑语意,并且实际中一般不会如此方式设计电路,所以暂时可以不支持反馈。

  表示

  于是,我们模块中所需要的就是要去表示上节提到的图。这就涉及到有向图该如何表示,实际上我们有很多不同的方法来表示。

  

  还是以这个图为例,

  以下几个list可以描述图中所有的顶点,

  (input-pin a)

  (input-pin b)

  (input-pin c)

  (output-pin d)

  (not-gate (a) (e))

  (or-gate (b c) f)

  (xor-gate (e f) (g))

  (and-gate (e g) (d))

  以上只是用Lisp的括号来表示的list,实际上并不是十分严格。其实这些也携带了有向图的各个边的信息,于是如果以上8个顶点的list分别为s1~s8,那么(s1 s2 s3 s4 s5 s6 s7 s8)就是整个电路图了(虽然如此效率比较低一点,因为边不是直接存储的,需要搜索)。

  接口

  类似于像第一章例子中构造异或这样的复杂门级那样,我们也可以模仿一下像以下这样定义本章例子电路模块,

(define (newmodule input output edge)
(let ((a (car input))
(b (cadr input))
(c (caddr input))
(d (car output))
(e (make-wire))
(f (make-wire))
(g (make-wire)))
(make-primitive-instance not-gate (list a) (list e))
(make-primitive-instance or-gate (list b c) (list f))
(make-primitive-instance xor-gate (list e f) (list g))
(make-primitive-instance and-gate (list e g) (list d))))

  

  这是希望和上一章的原语采用相同的语义。然而,和原语不一样的是,模块可以表示更复杂一些的电路:原语里的时序电路,所有的状态都在输出上;而更加复杂一些的电路,状态可能不止输出这些信号。

  比如以下verilog描述的模块

module test
(
reset,
clk,
en,
in,
out
);
input reset, clk, en, in;
output out; reg [:]cnt;
assign out = cnt[]; always@(posedge clk or posedge reset)
if(reset)
cnt <= 'b00;
else
cnt <= cnt + 'b01; endmodule

  其中的输出信号out并不代表着电路的所有状态,得再加上内部的cnt[0]在一起才是整体的状态(out是cnt[1])。

  

  于是,我们不得不去想,我们的module不大可能是和原语同一个接口了。我们回头再想一想,之前Scheme描述的原语实现的是无副作用的函数,也就是数学意义上的函数。而我们实际上可以引入副作用的方式来设计函数,我们可以把状态绑在module内部所有的wire上,这种方法第一章中提到过。

  那么,模块函数应该包含着建立上一节所提到的有向图结构以及建立相应每个wire的状态的信息。模块实例化则是函数返回一个带有副作用的闭包,参数edge是没有必要了,模块需要返回的最主要信息还是有向图结构信息,那么接口只需要删除掉edge,可以如下:

(define (newmodule input output)
(let ((a (car input))
(b (cadr input))
(c (caddr input))
(d (car output))
(e (make-wire))
(f (make-wire))
(g (make-wire)))
(make-primitive-instance not-gate (list a) (list e))
(make-primitive-instance or-gate (list b c) (list f))
(make-primitive-instance xor-gate (list e f) (list g))
(make-primitive-instance and-gate (list e g) (list d))))

  上面长的不太像数字电路设计,我们其实也可以考虑写成下面这样:

(module newmodule
(input a b c)
(output d)
(wire e f g)
(
(p not-gate (a) (e))
(p or-gate (b c)(f))
(p or-gate (e f) (g))
(p and-gate (e g) (d))
)
)

  这样的代码熟悉数字设计的朋友看起来会觉得比较熟悉,其中(p not-gate (a) (e))中最前面的p是用来区分原语和模块,如果填写字母m则代表模块,原因在于我这里原语和模块并没有统一,但如果统一了,则不需要这个标志了。

  包括Scheme在内的所有Lisp都有一种神奇的本领叫宏,让上述看起来面目全非的代码转换成之前要写的函数。

  其他

  本章只是提到了一些思想,其实我们还有很多可能需要继续改造或者直接放弃的地方,以下列出几点:

  1.系列并没有给出inout,没有三态门。

  2.线与逻辑似乎并不好实现。

  3.原语和模块没有统一。

  4.只能做实现级的描述,无法做像verilog/VHDL那样的RTL。其实这里可以引入宏,来展开比较复杂表达式。

  5.将来为了仿真的方便,不考虑支持反馈,毕竟反馈在数字设计里用处不大。

Scheme实现数字电路仿真(3)——模块的更多相关文章

  1. Scheme实现数字电路仿真(1)——组合电路

    EDA是个很大的话题,本系列只针对其中一小部分,数字电路的仿真,叙述一点概念性的东西,并不会过于深入,这方面的内容实则是无底洞.本系列并不是真的要做EDA,按照SICP里的相关内容,采用Lisp的方言 ...

  2. Scheme实现数字电路仿真(2)——原语

    版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖.如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/12045295.html 作者:窗户 ...

  3. Python 标准类库-数字和数学模块之decimal使用简介

    标准类库-数字和数学模块之decimal使用简介 by:授客 QQ:1033553122 例子 >>>from decimal import * >>>getcon ...

  4. 基于STM32的三轴数字罗盘HMC5883L模块的测试

    最近买了个数字罗盘模块,调通后发现很不错,非常灵敏,测试的时候精度在1°以内.连续测量模式下,最快测量.输出速率可达75hz,模块每次测量完毕并将数据更新至寄存器后,其DRDY引脚便产生一个低电平脉冲 ...

  5. python 各模块

    01 关于本书 02 代码约定 03 关于例子 04 如何联系我们 1 核心模块 11 介绍 111 内建函数和异常 112 操作系统接口模块 113 类型支持模块 114 正则表达式 115 语言支 ...

  6. Python 中的数字到底是什么?

    花下猫语:在 Python 中,不同类型的数字可以直接做算术运算,并不需要作显式的类型转换.但是,它的"隐式类型转换"可能跟其它语言不同,因为 Python 中的数字是一种特殊的对 ...

  7. PHPCMS v9构建模块

    ■补课: 1.phpcms v9帮助文件,上面会写关于二次开发的一些方法. http://v9.help.phpcms.cn/ 2.找一个后台还没安装的模块,先把代码看一边.比如dianping模块 ...

  8. 基于FPGA的数字识别的实现

    欢迎大家关注我的微信公众号:FPGA开源工作室     基于FPGA的数字识别的实现二 作者:lee神 1 背景知识 1.1基于FPGA的数字识别的方法 通常,针对印刷体数字识别使用的算法有:基于模版 ...

  9. [Swift]LeetCode715. Range 模块 | Range Module

    A Range Module is a module that tracks ranges of numbers. Your task is to design and implement the f ...

随机推荐

  1. mint 18中安装最新的R

    mint 中默认的R版本有点老,升级最新版方法如下: 先卸载 sudo apt-get remove r-base-core 添加mint 18 识别的源 sudo echo "deb ht ...

  2. 吴裕雄--天生自然TensorFlow高层封装:Keras-多输入输出

    # 1. 数据预处理. import keras from keras.models import Model from keras.datasets import mnist from keras. ...

  3. Appium获取元素的方式

    1.apk包名和launcherActivity 1.1.获取包名 所有应用包名列表 adb shell pm list packages 第三方应用包名列表 adb shell pm list pa ...

  4. c语言中fflush的运用为什么没有效果呢,测试平台linux

    /************************************************************************* > File Name: clearing. ...

  5. Opencv笔记(十五)——图像金字塔

    参考文献 目标 学习图像金字塔 学习函数cv2.pyrUp()和cv2.pyrDown() 原理 当我们需要将图像转换到另一个尺寸的时候, 有两种可能,一种是放大图像,另一种是缩小图像.尽管在Open ...

  6. G - Green-Red Tree Gym - 102190G

    题目链接:http://codeforces.com/gym/102190/attachments 题解:我们先将前5个点分别涂上红色或者绿色,使得这两棵树在5个点中都是连通,并不存在自环(建边方式不 ...

  7. sequelize 应用hook 实现对分表的访问

    https://github.com/cclient/sequelize-shardinghttps://www.npmjs.com/package/sequelize-sharding 实际有效的代 ...

  8. linux进程(二)

    信号管理进程使用kill命令发送信号与进程通信定义守护进程的角色结束用户会话的进程 kill,killall,pgrep,pkill 对于进程的正常关闭的理解正常关闭程序的方法systemctl st ...

  9. 给本地web项目配置域名

    给本地的web项目配置一个域名 通常访问本地问项目时,使用localhost:port/projectname或者127.0.0.1:port/projectname来实现.我们可以通过配置tomca ...

  10. xmemcached过期时间

    最近项目中使用到了Memcached,而客户端选择了XMemcached ,在设置过期时间时,因对Memcached 不熟悉,将expire 设置为1000000000,本意表示尽量长的时间不要过期, ...