用于 GAL16V8 的 CUPL 语言概要
安装
首先去 Microchip 网站下载 WinCupl 并安装. 第一次运行时, 需要输入注册码 60008009 (这个是官方放出来的). 安装前注意备份用户的 PATH 环境变量的值, 因为 WinCupl 安装程序可能会把这个环境变量覆盖掉.
文件头
按照八股文格式写文件头( WinCupl 可以自动生成), 注意 Device
字段要写 “g16v8a
”.
管脚定义
文件中首先要定义管脚, 如:
/* *************** INPUT PINS *********************/
pin [1,11]=[CLK, !OE]; pin 2=CIN;
/* *************** OUTPUT PINS *********************/
pin 19=COUT;
pin [16..17]=[Q1..0];
管脚的输入与输出
注意内部的 8 个 D 触发器的时钟都连接在 1 脚, 输出使能都连接在 11 脚; 1~9 和 11 脚只能用作输入, 12~19 脚可以用作输入、组合逻辑的输出或 D 触发器的输出.
管脚的极性
定义管脚时, 可以在前面用 !
指定极性. 如 11 脚为 !OE
, 表明 11 脚接低电平时 OE 为 'b'1
.
管脚定义的简写
定义时可以简写, 如
Pin [2..4] = [AD0..2];
相当于
Pin 2 = AD0;
Pin 3 = AD1;
Pin 4 = AD2;
管脚的方向
12~19 脚的方向可以自动判别(逻辑表达式中写在左边就自动变成输出了).
管脚的序号
管脚号和变量名称中的序号是十进制的.
组合逻辑
实现组合逻辑时, 直接写逻辑表达式即可. 管脚名后面加 .OE
是输出作为组合逻辑使用时的输出使能信号, 12~19 脚作双向管脚或三态输出管脚使用时能够用得上. 对于单向应用, .OE
可以自动设置.
如
COUT=!CIN;
逻辑表达式中等号左边的变量还可以加 !
, 表示负逻辑.
变量的写法
等号左边的变量可以是真实管脚, 也可以是虚构的中间变量(当然等号右边的变量也可以是虚构的中间变量). 如果是后一种情况, 在用到时, 综合器会将其代入.
逻辑表达式中的变量也可以简写, 如
[D0..3] = 'b'0010;
[D0..3].oe = read;
可以用 FIELD
将管脚框成一个字段, 如
FIELD input = [in3..0];
field ADDR = [VAR0, VAR1, VAR2];
逻辑运算符
逻辑运算符定义如下:
! NOT
& AND
\# OR
$ XOR
常数的格式
常数写类似 'b'0
, 'o'663
, 'd'92
, 'h'ba
的格式. 默认为 16 进制.
表达式的连接
表达式可以用 APPEND
进行连接. 多个 APPEND
的结果是 #
在一起的, 如
APPEND Y = A0 & A1;
APPEND Y = B0 & B1;
APPEND Y = C0 & C1;
相当于
Y = (A0 & A1) # (B0 & B1) # (C0 & C1);
相等算符
逻辑表达式中还可以用相等算符 “:
”. 如
select = [A3..0]:'h'D;
的意思是如果 A3..0
的值等于 'h'D
, 那么 select
为真, 否则 select
为假. 由于 'h'd
等于 'b'1101
, 所以上述表达式等价于
select = A3 & A2 & !A1 & A0 ;
相等算符中的常数还可以出现 X
, 表示某一部分不进行比较. 如
select = [A3..0]:'b'1X0X;
等价于
select = A3 & !A1;
注意: 如果写
FIELD ADDR = [VAR18, VAR17, VAR16];
ROM_SEL = ADDR:3;
那么 ROM_SEL
中比较的其实是 ADDR
的第 18、17 和 16 位. 所以
PIN [3..6] = [A7..10];
FIELD ioaddr = [A7..10];
io_port = ioaddr:[400..6FF];
展开化简的结果其实非常简单, 只有
io_port = A10 & !A9 # A10 & A9 & !A8 ;
一句.
写
field ADDR = [VAR0, VAR1, VAR2];
和
field ADDR = [VAR2, VAR1, VAR0];
在进行比较时是完全等价的. 这两种写法中,
ROM_SEL = ADDR:3;
都会展开成
ROM_SEL = !VAR2 & VAR1 & VAR0; .
逻辑表达式的简写
逻辑表达式的几种简写方式如下:
[D0, D1, D2, D3] & R
等价于[D0 & R, D1 & R, D2 & R, D3 & R]
;[A, B, C, D] & [W, X, Y, Z]
等价于[A & W, B & X, C & Y, D & Z]
;[A3,A2,A1,A0]:&
等价于A3 & A2 & A1 & A0
;adr:[C..F]
等价于adr:C # adr:D # adr:E # adr:F
.
查找表
某些情况下用查找表 (TABLE
) 的方式实现组合逻辑更方便, 如
FIELD input = [in3..0];
FIELD output = [out4..0];
TABLE input => output {
0=>00; 1=>01; 2=>02; 3=>03;
4=>04; 5=>05; 6=>06; 7=>07;
8=>08; 9=>09; A=>10; B=>11;
C=>12; D=>13; E=>14; F=>15;
}
条件判断
还可以用条件判断 (CONDITION
) 来写组合逻辑, 如
PIN [1,2] = [A,B]; /* Data Inputs */
PIN 3 = !enable; /* Enable Input */
PIN [12..15] = [Y0..3]; /* Decoded Outputs */
PIN 14 = no_match; /* Match Output */
CONDITION {
IF enable & !B & !A OUT Y0;
IF enable & !B & A OUT Y1;
IF enable & B & !A OUT Y2;
IF enable & B & A OUT Y3;
}
CONDITION
里面除了 IF expr OUT var;
之外, 还可以包含 DEFAULT OUT var;
语句. 使用 DEFAULT
这种写法时需要注意, 因为 DEFAULT
包含了与所有 IF 里面的逻辑表达式互补的情况, 所以可能会生成非常复杂的逻辑表达式.
时序逻辑
如果要实现时序逻辑, 每个输出有一个D触发器可以用, 输出名字加.D
是输入端.
如:
Q1.D=Q0;
Q0.D=!Q1;
状态机
实现时序逻辑还可以用状态机的方式. 状态机的基本框架如下:
SEQUENCE state_var_list {
PRESENT state_n0
IF (condition1) NEXT state_n1;
IF (condition2) NEXT state_n2 OUT var;
DEFAULT NEXT state_n0;
PRESENT state_n1
NEXT state_n2;
.
.
PRESENT state_nn statements;
}
其中 SEQUENCE
后面跟一个变量列表. 这个变量列表一般是用 FIELD
定义的一系列作触发器使用的输出管脚. 每个 PRESENT
后面跟一种状态.
状态转移
状态转移有两种写法: 无条件状态转移和条件状态转移.
无条件状态转移写法如
PRESENT 'b'01
NEXT 'b'10;
意义自明. 注意这种情况下每个 PRESENT
只能配一个 NEXT
. 条件状态转移写法如
PRESENT 'b'01
IF A & B NEXT 'b'10;
IF A & !B NEXT 'b'11;
DEFAULT NEXT 'b'00;
IF
的格式与 CONDITION
里面类似. DEFAULT
的使用注意事项也与 CONDITION
中类似.
异步输出与同步输出
状态机中的每一个状态还可以有异步输出和同步输出, 和上面的两种状态转移一起, 就有四种输出方式: 无条件同步、无条件异步、条件同步、条件异步. 注意 GAL16V8 的状态机输出不支持低有效(如 OUT !Y
)的写法.
无条件同步输出的写法如
PRESENT 'b'01
NEXT 'b'10 OUT Y OUT Z;
意义自明.
无条件异步输出的写法如
PRESENT 'b'01
OUT Y OUT Z;
NEXT 'b'10;
意义自明.
条件同步输出的写法如
PRESENT 'b'01
IF INA NEXT 'b'10 OUT Y;
IF !INA NEXT 'b'11 OUT Z;
意义自明.
条件异步输出的写法如
PRESENT 'b'01
IF INA OUT Y;
IF !INA OUT Z;
NEXT 'b'10;
意义自明.
条件输出语句中也可以包含 DEFAULT
.
助记法: 记住同步输出是 NEXT
之后输出, 异步输出与 NEXT
无关, 就全都明白了. 例如, 如果写成
PRESENT 5
NEXT 6;
OUT DIVOUT;
的形式, 是异步输出(注意 NEXT
后面有分号, 所以这个输出和 NEXT
无关); 写成
PRESENT 5
NEXT 6 OUT DIVOUT;
就是同步输出了. 异步输出到一个管脚的 .D
, 也有同步输出的功效, 如
PRESENT 5
NEXT 6;
OUT DIVOUT.D;
程序例子
一个完整的包含状态机的程序例子(双向十进制计数器, 带同步清零和异步进位输出, 取自 WinCupl 的例子)如下:
Name Count10;
Partno CA0018;
Date 12/19/99;
Revision 02;
Designer Kahl;
Company Logical Devices, Inc.;
Assembly None;
Location None;
Device g16v8a;
/****************************************************************/
/* Compiling Note: For this Design , it will compile without any*/
/* Errors if you Click on Options from the Main WInCUPL tool window */
/* and Select Compiler option and set Minimization button to QUICK*/
/* */
/* Decade Counter */
/* */
/* This is a 4-bit up/down decade counter with synchronous */
/* clear capability. An asynchronous ripple carry output is */
/* provided for cascading multiple devices. CUPL state machine */
/* syntax is used. */
/****************************************************************/
/** Inputs **/
Pin 1 = clk; /* Counter clock */
Pin 2 = clr; /* Counter clear input */
Pin 3 = dir; /* Counter direction input */
Pin 11 = !oe; /* Register output enable */
/** Outputs **/
Pin [14..17] = [Q3..0]; /* Counter outputs */
Pin 18 = carry; /* Ripple carry out */
/** Declarations and Intermediate Variable Definitions **/
field count = [Q3..0]; /* declare counter bit field */
$define S0 'b'0000 /* define counter states */
$define S1 'b'0001
$define S2 'b'0010
$define S3 'b'0011
$define S4 'b'0100
$define S5 'b'0101
$define S6 'b'0110
$define S7 'b'0111
$define S8 'b'1000
$define S9 'b'1001
field mode = [clr,dir]; /* declare mode control field */
up = mode:0; /* define count up mode */
down = mode:1; /* define count down mode */
clear = mode:[2..3]; /* define count clear mode */
/** Logic Equations **/
Sequenced count { /* free running counter */
present S0 if up next S1;
if down next S9;
if clear next S0;
if down out carry;
present S1 if up next S2;
if down next S0;
if clear next S0;
present S2 if up next S3;
if down next S1;
if clear next S0;
present S3 if up next S4;
if down next S2;
if clear next S0;
present S4 if up next S5;
if down next S3;
if clear next S0;
present S5 if up next S6;
if down next S4;
if clear next S0;
present S6 if up next S7;
if down next S5;
if clear next S0;
present S7 if up next S8;
if down next S6;
if clear next S0;
present S8 if up next S9;
if down next S7;
if clear next S0;
present S9 if up next S0;
if down next S8;
if clear next S0;
if up out carry; /* assert carry output */
}
注意 $DEFINE
是预处理器指令, 功效与 C 的 #define
相同.
结语
大概没了. 对这种简单可编程逻辑器件, 别的高级功能也没啥用. WinCupl 中自带的仿真工具 WinSim 使用很简单. 不明确的地方请参见 CUPL 用户手册.