Scheme实现数字电路仿真(3)——模块
版权申明:本文为博主窗户(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)——模块的更多相关文章
- Scheme实现数字电路仿真(1)——组合电路
EDA是个很大的话题,本系列只针对其中一小部分,数字电路的仿真,叙述一点概念性的东西,并不会过于深入,这方面的内容实则是无底洞.本系列并不是真的要做EDA,按照SICP里的相关内容,采用Lisp的方言 ...
- Scheme实现数字电路仿真(2)——原语
版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖.如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/12045295.html 作者:窗户 ...
- Python 标准类库-数字和数学模块之decimal使用简介
标准类库-数字和数学模块之decimal使用简介 by:授客 QQ:1033553122 例子 >>>from decimal import * >>>getcon ...
- 基于STM32的三轴数字罗盘HMC5883L模块的测试
最近买了个数字罗盘模块,调通后发现很不错,非常灵敏,测试的时候精度在1°以内.连续测量模式下,最快测量.输出速率可达75hz,模块每次测量完毕并将数据更新至寄存器后,其DRDY引脚便产生一个低电平脉冲 ...
- python 各模块
01 关于本书 02 代码约定 03 关于例子 04 如何联系我们 1 核心模块 11 介绍 111 内建函数和异常 112 操作系统接口模块 113 类型支持模块 114 正则表达式 115 语言支 ...
- Python 中的数字到底是什么?
花下猫语:在 Python 中,不同类型的数字可以直接做算术运算,并不需要作显式的类型转换.但是,它的"隐式类型转换"可能跟其它语言不同,因为 Python 中的数字是一种特殊的对 ...
- PHPCMS v9构建模块
■补课: 1.phpcms v9帮助文件,上面会写关于二次开发的一些方法. http://v9.help.phpcms.cn/ 2.找一个后台还没安装的模块,先把代码看一边.比如dianping模块 ...
- 基于FPGA的数字识别的实现
欢迎大家关注我的微信公众号:FPGA开源工作室 基于FPGA的数字识别的实现二 作者:lee神 1 背景知识 1.1基于FPGA的数字识别的方法 通常,针对印刷体数字识别使用的算法有:基于模版 ...
- [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 ...
随机推荐
- Android开发学习2--Android Studio目录结构、Module目录介绍、Android创建及运行和HelloWord的扩展----极其简单的游戏界面
学习笔记: 1.Android Studio项目结构 Android Studio提供了很多项目结构,最常用的是Android 和 project Project列举出了所有文件. 建议使用Andro ...
- winform上传文件,利用http,form-data格式上传
/// <summary> /// 上传文件 /// </summary> /// <param name="url">服务地址</par ...
- 微服务监控druid sql
参考该文档 保存druid的监控记录 把日志保存的关系数据数据库(mysql,oracle等) 或者nosql数据库(redis,芒果db等) 保存的时候可以增加微服务名称标识好知道是哪个微服务的sq ...
- linux下用firefox打开csdn故障解决办法
问题:浏览器打开csdn时博客浏览不全,没有了排版 解决办法: 1.下载一个安装包 命令:epel-release-7-11.noarch.rpm: epel-release-7-11.noarch ...
- springboot-security 登录 403
之前一直使用shiro,刚开始使用security,大佬还请不要吐槽 security默认开启csrf防护,所谓csrf也就是伪请求.我们只需要把他关闭就好(因为我们的系统是在自己内网使用,不会有外部 ...
- 在Myeclipse10中配置tomcat后新建工程
1.配置tomcat6.0 这里不在细说,和eclipse配置是一模一样的. 2.新建动态网站项目 3.配置显示服务器窗口 4.把项目与服务器链接 5.运行项目
- macbook安装LightGBM
一开始直接用pip install lightgbm 报错: OSError: dlopen(/opt/anaconda3/lib/python3.7/site-packages/lightgbm/l ...
- winform显示word、ppt和pdf,用一个控件显示
思路:都以pdf的格式展示,防止文件拷贝,所以要把word和ppt转换为pdf:展示用第三方组件O2S.Components.PDFView4NET.dll,破解版的下载链接:https://pan. ...
- ABC:Meaningful Mean
题目描述 You are given an integer sequence of length N, a= {a1,a2,…,aN}, and an integer K. a has N(N+1)⁄ ...
- PostgreSQL与mysql的比较
特性 MySQL PostgreSQL 实例 通过执行 MySQL 命令(mysqld)启动实例.一个实例可以管理一个或多个数据库.一台服务器可以运行多个 mysqld 实例.一个实例管理器可以监视 ...