版权申明:本文为博主窗户(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. day52-线程-队列

    #1.线程的队列是使用import queue,如果使用from threading import Queue会报错,因为threading模块没有Queue. #也就是说,线程队列Queue是在qu ...

  2. 题解 P3061 【[USACO12DEC]疯狂的栅栏Crazy Fences】

    这道题的思想是首先我们找到所有的栅栏围成的空间,然后求每一只奶牛在哪几个栅栏空间之中,最后比较他们在的所有栅栏空间-----如果奶牛a和b同时在空间c,d和e内,那么他们一定在同一群中. 测试围栏的方 ...

  3. winform把所有dll打包成一个exe

    大家都知道做winform开发,是可以利用visual studio进行打包的,但是这种打包的方式需要双击安装,那么有没有什么方法,可以把winform程序打包成绿色版呢?当然,这里的“绿色版”也是相 ...

  4. 启动查看crontab日志服务

    方法1: . 修改rsyslog文件,将/etc/rsyslog.d/-default.conf 文件中的#cron.*前的#删掉: . 重启rsyslog服务service rsyslog rest ...

  5. C - Line-line Intersection Gym - 102220C(线段相交)

    There are n lines l1,l2,…,ln on the 2D-plane. Staring at these lines, Calabash is wondering how many ...

  6. day23-logging模块

    # logging日志记录的两个内容:1.有5种级别的日志记录模式.2.两种配置方式:basicconfig.logger对象. # logging的作用: #1.排错的时候需要打印很多细节来帮助排错 ...

  7. 牛客-富豪凯匹配串(bitset)

    题目传送门 sol1:用bitset来维护,其实感觉挺暴力的,不怎么会用bitset,借着这道题学习一下. bitset暴力维护 #include "bits/stdc++.h" ...

  8. php的header方法

    http://www.cnblogs.com/fengzheng126/archive/2012/04/21/2461475.html

  9. python语法基础-函数-递归函数-长期维护

    ###############    递归   ############## # 递归的定义——在一个函数里再调用这个函数本身 # 递归的最大深度——998 # 二分查找算法 # 你观察这个列表,这是 ...

  10. Django常见错误总结

    1 ImportError: No module named 'MySQLdb' 解决方法: . 安装pymysql模块 . 在app的__init__.py文件中写入以下内容 import pymy ...