用于 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; .

逻辑表达式的简写

逻辑表达式的几种简写方式如下:

  1. [D0, D1, D2, D3] & R 等价于 [D0 & R, D1 & R, D2 & R, D3 & R];
  2. [A, B, C, D] & [W, X, Y, Z] 等价于 [A & W, B & X, C & Y, D & Z];
  3. [A3,A2,A1,A0]:& 等价于 A3 & A2 & A1 & A0;
  4. 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 用户手册.