CPLDとはプログラマブルロジックと呼ばれるデバイスの1つで、中身を書き換えることによってちょっとした組み合わせ回路や順序回路を作ることができます。立ち位置としてはGALよりもリッチで、FPGAよりもショボいという感じです。
今回はマルツにて在庫処分セールで1個72円で売っていたATF16V8BQL-15PUを用いて、自作三進CPUで使用する三進↔二進変換回路を作成してみました。
CPLDというデバイスの制約や、開発ツールの不具合に悩まされましたが、色々と新しい知見を得ることができたので記事にまとめたいと思います。
なお、この記事に関する開発物はこちらのGitHubリポジトリに置いてあるので良かったら参考にしてください。
目次
作りたいもの
自作三進CPUではレジスタ等はちゃんと5 V, 0 V, -5 Vの3状態を使って保持していますが、メモリに関してはどうしても二進のものを使用することになります。
そのため三進数を二進数に変換する必要があり、現状としては下の表のように1 trit(三進1桁のこと)を二進2 bitに変換することによって5 tritのアドレスやデータを10 bitに変換しています。これをBinary Coded Ternary (BCT)と呼ぶことにします。
| 三進 | 二進 |
|---|---|
| 1 | 10 |
| 0 | 00 |
| -1 | 01 |
3^5 = 243 < 2^8なので、本当は8 bitに収まるのですが、BCTでは10 bitとなります。
現状は10 bitのBCTをメモリ代わりのマイコンで読み書きしていますが、将来的にマイコンを排除してメモリICを使うためには、冗長性を排除して8 bitに収める必要があります。
そのため、作るべきものは「10入力8出力の組み合わせ回路、ただし入力は2^10 = 1024通りのうち243通りしか登場しない」となります。また、逆変換の組み合わせ回路も作りたいです。
この変換はかなり複雑な真理値表になるので、どう実現するかに悩んでいました。最初に思いつくのはFPGAですが、安価なTang Nano 9Kでも数千円するので複数個使うことを思うと気が引けます。そんな折、このCPLDを見つけて安く作れるのではと考えました。
CPLDの仕組み
まずはCPLDに何ができるのかを知るために、内部回路の仕組みを調査しました。こちら(https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-0364-PLD-ATF16V8B-8BQ-8BQL-Datasheet.pdf) のデータシートから図等を引用します。
このCPLDにはRegister, Complex, Simpleの3つの動作モードがあり、フリップフロップを使う場合はRegisterモード、フリップフロップは使わないが出力イネーブルピンを使う場合はComplexモード、出力イネーブルピンも使用しない場合はSimpleモードを使用します。
今回は単純な組み合わせ回路なので、Simpleモードを使用します。
ヒューズアレイ
CPLDがプログラマブルな動作を実現する仕組みは下図(一部分を示しています)のヒューズアレイです。

まず縦に伸びるInput Linesが32本あり、その内訳は、外部入力ピン8本、その反転8本、Output Logicのフィードバック等8本、その反転8本となっています。
横の線に繋がるANDゲートは図では1入力に見えますが、これは省略されていて、実際には32入力でそれぞれのInput Linesに対応しています。
縦と横の線が交差するところにはヒューズ(実際にはリセット可能な電気的なスイッチ)があり、ANDゲートにどの信号を入力するかを選択することができます。
なおRegisterモードやComplexモードでも基本は同じですが、使用できるANDゲートの数やフィードバック信号の出処等が少し違います。
出力ブロック
SimpleモードにおけるOutput Logicの中身は以下のようになっています。

8個のANDゲートの値がORゲートに入力されます。これによって、論理式を積和標準形に対応する形で表現できます。
図中のXORは選択的に出力を反転するためのもので、1を作るための論理式より0を作る論理式が単純な場合等に使われます。
出力を有効化するかは(プログラム時に)選択可能で、出力しない場合はそのピンを入力として利用可能です。
なおRegisterモードではその名の通りここにフリップフロップがあったりします。
開発環境
CPLDの開発にはCUPLという言語を使用します。CUPLのコンパイルやシミュレーションにはWinCUPL IIというツールを使用できます。
コンパイル結果をICに書き込むにはプログラマーが必要ですが、汎用的なプログラマーは結構値が張るので、Afterbunerというプロジェクトを参考に自作しました。2000円弱で作れたと思います。
とりあえず作ってみる
Simpleモードでは入力専用ピンが10本、出力ブロックが繋がる入出力ピンが8本あります。回路規模的に難しそうではありますが、ピン数的には10入力8出力の真理値表が作れるのでダメ元で試しに書いてみることにします。
WinCUPL IIを起動しプロジェクトを新規作成して、Deveice FamilyにATF16V8BQL、Package Typeに20-DIP、OptionにSimpleを指定します。

プロジェクトが開かれると、下図のようにGUIでデザインフローが示されていて、Design File ManagerのNEW/EDITを押すと、CUPLを書くpldファイルが開かれます。
ただこの内蔵エディタは使いづらいので、作成されたpldファイルをvscode等のエディタで編集した方が使いやすいと思います。

pldファイルに以下のようにコードを書きます。一部省略しているので、全体を見たい場合は こちら からどうぞ。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
PIN [1..9] = [A9..1]; PIN 11 = A0; PIN [19..12] = [Y7..0]; TABLE [A9..0] => [Y7..0] { 'b'0101010101 => 'b'00000000; 'b'0101010100 => 'b'00000001; 'b'0101010110 => 'b'00000010; 'b'0101010001 => 'b'00000011; 'b'0101010000 => 'b'00000100; ... 'b'1010101000 => 'b'11110001; 'b'1010101010 => 'b'11110010; } |
CUPLではANDやORに対応する演算を直接書くほかに、このようにTABLEを用いて真理値表を直接書くこともできます。論理式への変換と最適化はコンパイラがやってくれます。
なおこのテーブルは こちら のPythonスクリプトで生成しています。
WinCUPL IIでDesign CompilationのCOMPILEを押すとコンパイルが実行されますが、excessive number of product termsというメッセージとともに失敗します。
product termというのは複数入力をAND演算した項のことで、ORに入力される値となります。先ほど見た内部回路としてはこれが8個以下に収まる必要があるのですが、それを超えてしまっているということです。
WinCUPL IIのCompiler OptionsのOutput Filesで下図のようにEquationsにチェックを入れると、どのような積和演算に変換されたかを確認することができます。

出力されたdocファイルを確認すると、次のようになっていました。
Y0 =>
!A0 & !A1 & !A2 & A3 & !A4 & A5 & !A6 & A7 & !A8 & A9
# !A0 & !A1 & A2 & !A3 & A4 & !A5 & A6 & !A7 & A8 & !A9
# A0 & !A1 & !A2 & !A3 & A4 & !A5 & A6 & !A7 & A8 & !A9
# !A0 & A1 & !A2 & !A3 & A4 & !A5 & A6 & !A7 & A8 & !A9
...
1行が1つのproduct termに対応していて、最も項数の多いY0では121項にもなっていました。
Compiler OptionsのMinimizationのアルゴリズムを変えることで多少小さくはなりましたが、8項に収めるのは到底無理なので別の戦略を考える必要があります。
困難は分割せよ
項数が膨れ上がってしまうのは、回路の複雑性が入力信号数に対して指数的に増えていくことが原因です。そこで、変換を2段階に分割することにします。
まずは3 tritに対応するBCT 6 bit (B5..0)を0 ~ 26の5 bit (X4..0)に変換することを考え、以下のコードを作成しました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
PIN [1..6] = [B5..0]; PIN [19..15] = [X4..0]; TABLE [B5..0] => [X4..0] { 'b'010101 => 'b'00000; 'b'010100 => 'b'00001; 'b'010110 => 'b'00010; 'b'010001 => 'b'00011; 'b'010000 => 'b'00100; 'b'010010 => 'b'00101; 'b'011001 => 'b'00110; 'b'011000 => 'b'00111; 'b'011010 => 'b'01000; 'b'000101 => 'b'01001; 'b'000100 => 'b'01010; 'b'000110 => 'b'01011; 'b'000001 => 'b'01100; 'b'000000 => 'b'01101; 'b'000010 => 'b'01110; 'b'001001 => 'b'01111; 'b'001000 => 'b'10000; 'b'001010 => 'b'10001; 'b'100101 => 'b'10010; 'b'100100 => 'b'10011; 'b'100110 => 'b'10100; 'b'100001 => 'b'10101; 'b'100000 => 'b'10110; 'b'100010 => 'b'10111; 'b'101001 => 'b'11000; 'b'101000 => 'b'11001; 'b'101010 => 'b'11010; } |
これのコンパイル結果はまたしてもexcessive number of product termsとなりましたが、X0が13項、X1が9項でX2, X3, X4は8項以下に収まっています。
CPLDが持つ8個の出力ブロックのうち5個しか使っていないので、余っているブロックとフィードバックを使えばどうにかなりそうです。
ということで以下のコードを作成しました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
PIN [1..6] = [B5..0]; PIN [19..15] = [X4..0]; PIN 14 = Z1; PIN 13 = Z0; Z0 = !B0 & !B1 & !B2 & B3 & !B4 & B5 # !B0 & !B1 & B2 & !B3 & B4 & !B5 # B0 & !B1 & !B2 & !B3 & B4 & !B5 # !B0 & B1 & !B2 & !B3 & B4 & !B5 # !B0 & !B1 & !B2 & B3 & B4 & !B5 # B0 & !B1 & B2 & !B3 & !B4 & !B5 # !B0 & B1 & B2 & !B3 & !B4 & !B5; X0 = Z0 # !B0 & !B1 & !B2 & !B3 & !B4 & !B5 # B0 & !B1 & !B2 & B3 & !B4 & !B5 # !B0 & B1 & !B2 & B3 & !B4 & !B5 # !B0 & !B1 & B2 & !B3 & !B4 & B5 # B0 & !B1 & !B2 & !B3 & !B4 & B5 # !B0 & B1 & !B2 & !B3 & !B4 & B5; Z1 = !B0 & B1 & !B2 & B3 & !B4 & B5 # !B0 & B1 & B2 & !B3 & !B5 # B0 & !B1 & !B2 & B4 & !B5 # !B0 & !B1 & !B2 & B3 & B4 & !B5 # !B0 & !B1 & B2 & !B3 & !B4 # !B0 & B1 & !B2 & !B3 & !B4 # B0 & !B1 & !B2 & B3 & !B4 & !B5; X1 = Z1 # B0 & !B1 & B2 & !B3 & !B4 & B5 # !B0 & !B1 & !B2 & !B3 & !B4 & B5; X2 = !B0 & B1 & B2 & !B3 & !B4 & B5 # !B0 & !B2 & !B3 & B4 & !B5 # !B1 & !B2 & B3 & B4 & !B5 # !B1 & !B2 & !B3 & !B4 # !B0 & B1 & !B2 & !B3 & !B4 # B0 & !B1 & !B2 & B3 & !B4 & !B5; X3 = !B0 & !B2 & B3 & !B4 & B5 # !B0 & B1 & !B2 & B3 & B4 & !B5 # B0 & !B1 & !B2 & B3 & !B4 # !B0 & B1 & !B3 & !B4 & !B5 # !B1 & !B3 & !B4 & !B5; X4 = !B0 & B1 & !B2 & B3 & !B4 & B5 # !B0 & !B2 & B3 & !B4 & !B5 # !B1 & !B2 & B3 & !B4 & B5 # !B0 & B1 & !B3 & !B4 & B5 # !B1 & !B3 & !B4 & B5; |
Z1, Z0が中間値です。X0の13項のうち8項をZ0にまとめてからX0の入力にフィードバックすることによって、どの出力ブロックも8項以下に抑えることができています。
注意点として、Z1やZ0をピンに割り当てないと、単なる内部変数として論理式をまとめられてしまうので明示する必要があります。また、Simpleモードではフィードバックに使えるピンと使えないピンがあるのでそこも確認する必要があります。
シミュレーション
WinCUPL IIではシミュレーションも実行できるので、コンパイル成功後はシミュレーションできちんと期待する動作になっているかを確認します。
後述しますが、コンパイルは成功してるのに結果がおかしいことがあったりするのでシミュレーションは絶対やった方がいいと思います。(これも後述しますが、コンパイル結果が間違ってるのにシミュレーションでは正しく動くこともあるんですけどね。クソが)
組み合わせ回路のシミュレーションでは、以下のように入力信号を0/1、期待する出力をH/Lで並べます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
ORDER: B5,B4,B3,B2,B1,B0,X4,X3,X2,X1,X0; VECTORS: 010101 LLLLL 010100 LLLLH 010110 LLLHL 010001 LLLHH 010000 LLHLL 010010 LLHLH 011001 LLHHL 011000 LLHHH 011010 LHLLL 000101 LHLLH 000100 LHLHL 000110 LHLHH 000001 LHHLL 000000 LHHLH 000010 LHHHL 001001 LHHHH 001000 HLLLL 001010 HLLLH 100101 HLLHL 100100 HLLHH 100110 HLHLL 100001 HLHLH 100000 HLHHL 100010 HLHHH 101001 HHLLL 101000 HHLLH 101010 HHLHL |
このコードをsiファイルに書き、WinCUPL IIでDesign SimulationのSIMULATEを押すとシミュレーションが実行されます。失敗した場合にはどのように違ったかがsoファイルに書き込まれます。
分割その後
3 tritを5 bitに変換することができました。残りは2 tritですが、3^2 = 9なので、二進数で表すには4桁必要です。先ほどの結果と合わせると5 + 4 = 9 bitとなり8 bitに収めることはできません。困りましたね。
しかしここで諦めるわけにはいきません。うまく8 bitに収める方法を考えます。
0 ~ 26の5 bitをX4..0、0 ~ 8の4 bitをA3..0とします。以下に取りうる値のビットパターンを示します。
特にA3が顕著ですが、A3はほとんど0なのに他の桁と同じ1 bitの大きさを持っていて、これは無駄がありそうです。
| N | A3..0 |
|---|---|
| 0 | 0 0 0 0 |
| 1 | 0 0 0 1 |
| ⋮ | ⋮ |
| 7 | 0 1 1 1 |
| 8 | 1 0 0 0 |
| N | X4..0 |
|---|---|
| 0 | 0 0 0 0 0 |
| ⋮ | ⋮ |
| 15 | 0 1 1 1 1 |
| 16 | 1 0 0 0 0 |
| ⋮ | ⋮ |
| 23 | 1 0 1 1 1 |
| 24 | 1 1 0 0 0 |
| 25 | 1 1 0 0 1 |
| 26 | 1 1 0 1 0 |
情報の符号化には様々な方法がありますが、効率的な符号化の鉄則は、よく出るパターンには短い符号を割り当てることです。これに従って、次のような符号化を考えてみましょう。
| Y7 | Y6 | Y5 | Y4 | Y3 | Y2 | Y1 | Y0 | 条件 |
|---|---|---|---|---|---|---|---|---|
| A2..0 | 0 | X3..0 | A3 = 0, X4 = 0 | |||||
| A2..0 | 1 | 0 | X2..0 | A3 = 0, X4..3 = 10 | ||||
| A2..0 | 1 | 1 | 0 | X1..0 | A3 = 0, X4..2 = 110 | |||
| X4..2 | 1 | 1 | 1 | X1..0 | A3 = 1 | |||
Y4..2の部分に可変長の符号を割り当てています。この符号によって条件を表し、それ以外の部分にAやXの残りの値を入れることで、きれいに8 bitに収めることができました。
できるはずだという直感に従ってやってみたらできた、という感じです。
注目すべき点として、Y1, Y0にはどの条件においてもX1, X0が入るので、この部分は計算する必要がありません。
したがって2段目の変換では残りのBCT 4 bitと、1段目の変換結果のうちのX4..2の3 bitを入力として、Y7..2を出力する回路を作ればよいことになります。
これについても、余っている出力ブロックによるフィードバックを利用することで、回路資源に収めることができました。コードは以下のようになっています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
PIN [1..4] = [BB3..0]; /* [B9..6] does not work well, so use [BB3..0] instead. */ PIN [5..7] = [XX2..0]; /* [X4..2] */ PIN [19..14] = [YY5..0]; /* [Y7..2] */ PIN 13 = Z0; PIN 12 = Z1; YY0 = XX0 & !XX1 # XX0 & !XX2 # !BB0 & BB1 & !BB2 & BB3; YY1 = !XX0 & XX1 # XX1 & !XX2 # !BB0 & BB1 & !BB2 & BB3; YY2 = !XX0 & XX2 # !XX1 & XX2 # !BB0 & BB1 & !BB2 & BB3; Z0 = !BB0 & BB1 & !BB2 & BB3 & XX0 # !BB0 & !BB1 & !BB2 & BB3 & !XX0 # !BB0 & !BB1 & BB2 & !BB3 & !XX0 # !BB0 & BB1 & !BB2 & !BB3 & !XX0 # BB0 & !BB1 & !BB2 & !BB3 & !XX0 # !BB0 & !BB1 & !BB2 & BB3 & !XX1 # !BB0 & !BB1 & BB2 & !BB3 & !XX1 # !BB0 & BB1 & !BB2 & !BB3 & !XX1; YY3 = Z0 # BB0 & !BB1 & !BB2 & !BB3 & !XX1 # !BB0 & !BB1 & !BB2 & BB3 & !XX2 # !BB0 & !BB1 & BB2 & !BB3 & !XX2 # !BB0 & BB1 & !BB2 & !BB3 & !XX2 # BB0 & !BB1 & !BB2 & !BB3 & !XX2; Z1 = BB0 & !BB1 & !BB2 & !XX0 # !BB1 & !BB2 & BB3 & !XX0 # BB0 & !BB1 & !BB2 & !XX1 # !BB1 & !BB2 & BB3 & !XX1 # BB0 & !BB1 & !BB2 & !XX2 # !BB1 & !BB2 & BB3 & !XX2 # !BB0 & BB1 & BB2 & !BB3 & !XX0 # !BB0 & BB1 & !BB2 & BB3 & XX1; YY4 = Z1 # !BB0 & BB1 & BB2 & !BB3 & !XX1 # !BB0 & BB1 & BB2 & !BB3 & !XX2; YY5 = !BB1 & !BB2 & BB3 & !XX0 # !BB0 & !BB2 & !BB3 & !XX0 # !BB1 & !BB2 & BB3 & !XX1 # !BB0 & !BB2 & !BB3 & !XX1 # !BB1 & !BB2 & BB3 & !XX2 # !BB0 & !BB2 & !BB3 & !XX2 # !BB0 & BB1 & !BB2 & BB3 & XX2; |
コード中のコメントにあるように、本当はB9..6の名前を使いたかったんですが、0始まりでない変数を使うと何故かうまくいかないので、気を付けてください。
コンパイルエラーは出ないんですが、論理式の展開結果が普通におかしくなりました。こういうことがあるのでシミュレーションはちゃんとやりましょう。
逆変換回路
BCT 10 bit → 二進8 bitの変換は2個のCPLDで実現できました。もとの三進の値と変換後の二進の値はバラバラな対応になってしまっていますが、一対一に対応さえしていればCPU側からは何も問題ありません。
そのため次は逆変換の回路を作る必要がありますが、こちらも2個のCPLDを使って、それぞれの変換の逆変換を組み合わせることで実現できました。
やるだけだったのでコードは割愛します。
順変換及び逆変換のICレベルでの構成図は以下のようになりました。

実機テスト
CPLD 4つ分のコンパイル、シミュレーションに成功したので、満を持して実機に書き込みました。
無事に書き込みにも成功したので実機テストを行います。マイコンにArduino互換機のPro Microボードを使用し、10 bitのBCTの243全パターンをテストします。BCT→二進→BCTとCPLDで変換して、入力値と出力値が一致することを確かめます。
GPIOが足りないので、その辺にあった74HC595を使って拡張しました。ジャンパワイヤだらけですが、テスト回路の様子はこんな感じです。

デバッグ祭り
実機テストの結果、全然正しく動きませんでした…。
とりあえず1段目だけ取り出して入力パターンを固定し、信号を一つずつ追っていきました。するとフィードバックに使っているピンが正しく動いてなさそうということが分かりました。
デバイスの仕様を勘違いしたか?と思い何度もデータシートを確認しましたが、フィードバックの使い方は間違ってなさそうです。
フィードバック周りの挙動を確認するために以下のような非常にシンプルな回路で試してみました。
|
1 2 3 4 5 6 |
PIN [5..6] = [B1..0]; PIN 15 = X0; PIN 13 = Z0; Z0 = B1; X0 = !Z0 & B0; |
これでも正しく動きません。これはいよいよコンパイラが間違っているのでは?という気がしてきました。
試しにデバイスの動作モードをSimpleではなくComplexにしてみたところ、ちゃんと動きます。
ただComplexモードではproduct termが7項に制限され、足りない部分がでてきてしまうので、どうにかSimpleモードで動かしたいです。
ここまでの調査の結果、Simpleモードではフィードバックに使うピンが出力ではなく入力に設定され、かつ入力ピンは内部でプルアップされていることが分かりました。
人力コンパイル
最終手段ということでコンパイル結果のバイナリ(ほぼテキストファイルですが)を見てみることにしました。
コンパイル結果はjedファイルで、以下のような内容になっています。
CUPL(WM) 5.0a Serial# MW-10400000
Device g16v8s Library DLIB-h-40-9
Created Wed Jul 01 23:27:01 2026
Name bct2bin.pld
Partno
Revision
Date
Designer
Company
Assembly
Location
*QP20
*QF2194
*G0
*F0
*L00000 10010111101101111011111111111111
*L00032 10100111101111111011111111111111
*L00064 10010111101110111111111111111111
*L00096 10011011111101111011111111111111
*L00128 10011011111110111111111111111111
*L00256 10010111101111111011111111111111
*L00288 01100111101101111011111111111111
*L00320 10110111101110110111111111111111
*L00352 10101011111101111011111111111111
*L00384 10101011111110111111111111111111
*L00512 10011011011101111011111111111111
*L00544 01101011101111111011111111111111
*L00576 01100111101110111111111111111111
*L00608 10111011101110111111111111111111
*L00640 10111011101101111011111111111111
*L00672 10100111101110110111111111111111
*L00768 11111111111111111101111111111111
*L00800 10011011011110110111111111111111
*L00832 10011011101110111011111111111111
*L01024 11111111111111111111110111111111
*L01056 10101011101110111011111111111111
*L01088 10100111101110110111111111111111
*L01120 10100111101101111011111111111111
*L01152 10011011011110111011111111111111
*L01184 10011011101110110111111111111111
*L01216 10011011101101111011111111111111
*L01280 10010111101101111011111111111111
*L01312 11101011011101111011111111111111
*L01344 01101111101110110111111111111111
*L01376 01100111101110111011111111111111
*L01408 10111011011110111011111111111111
*L01440 10111011101101111011111111111111
*L01472 10100111101110110111111111111111
*L01536 10010111101110111011111111111111
*L01568 01101011011110111011111111111111
*L01600 01101011101110110111111111111111
*L01632 01101011101101111011111111111111
*L01664 01100111101110111011111111111111
*L01696 10101011011110110111111111111111
*L01728 10101011011101111011111111111111
*L02048 11111110000000000000000000000000
*L02112 00000000000001111111111111111111
*L02144 11111111111111111111111111111111
*L02176 111111111111111110
*C9FAA
*9B66
この01の列が何に対応するかというのが感じですが、こちら (https://www.official.cz/static/_dokumenty/9/3/6/9/GAL16V8x.pdf) のデータシートに載っていました。
Simpleモードのアドレスは以下の図に書かれています。

ヒューズアレイのところに0000とか0224とか書かれていて、交点のヒューズに順番に対応していることが分かります。先ほどのバイナリと比べても間違いなさそうです。
入出力を制御しているのは出力ブロックのAC1というビットで、2120 ~ 2127がそれです。さっきのバイナリからここを見てみると…
*L02112 00000000000001111111111111111111
ビンゴ!フィードバックに使っているピンのAC1が、出力の0ではなく、入力の1になってました!
ここを0に書き換えてみます。ただそのままだとチェックサムが壊れて書き込めないので、Cから始まっている *C9FAAという行を消します。チェックサムは無くても書き込めました。
4つのCPLD全部対応して、改めてテストすると、全パターンでテストに通りました!!
まとめ
CPLDというプログラマブルロジックを使ってBCT↔二進の変換回路を作りました。
色々と頭を使うことで順変換と逆変換をIC 2個ずつで実装でき、かなりハードウェア資源を上手く活用できたと思います。
コンパイラにバグがあったせいでかなりデバッグに苦しみました。クソが
値段の安さ、ピンの多さ、5 V駆動といったあたりで、ピンポイントで刺さる場面があるんじゃないかなと思います。皆さんも是非CPLDを使ってみてはいかがでしょうか。





