
Утилита ctc для тестера микросхем поддерживает написание тестов на языке Lua.
Интерпретатор Lua встроен не в сам тестер, а в утилиту ctc — то есть скрипт выполняется на компьютере,
а тестер используется как интерфейс ввода-вывода.
Это позволяет писать собственные тесты для микросхем, которых нет в стандартной базе, или отлаживать сложные сценарии проверки (например, для БИС вроде 8251, 8259, Z80 и т.д.).
Простейший скрипт:
setChipSize(28)
bus('D', 27, 28, 1, 2, 5, 6, 7, 8)
pin('CLK', 20)
pin('~RESET', 21)
configOutput('CLK', '~RESET')
configInput('D')
enablePower(-4, 26)
set('~RESET')
pulseDown('CLK')
clr('~RESET')
Этот скрипт:
D из 8 линий.CLK и ~RESET.CLK и ~RESET как выходы, D — как вход.Запуск скрипта:
ctc z80.lua
| Команда | Назначение |
|---|---|
ctc script.lua | Выполнить скрипт. |
ctc -port COMx script.lua | Выполнить скрипт с указанием com-порта (если он не определяется автоматически). |
ctc lua | Запустить REPL (интерактивный режим). Выход — Ctrl-D. |
ctc lua power.lua | Запустить REPL с предварительной загрузкой скрипта. |
В REPL работает автодополнение по клавише TAB.
print и printf выводят текст в консоль. При ошибке в скрипте выполнение прерывается.
| Функция | Назначение |
|---|---|
setChipSize(n) | Задаёт количество выводов у тестируемой микросхемы (например, 28 или 40). Вызывается первым. |
disable() | Отключает все выводы (переводит в высокоимпедансное состояние). Обычно вызывается перед началом работы. |
disablePower() | Отключает питание, поданное через модуль ключей. |
| Функция | Назначение |
|---|---|
pin(name, n) | Объявляет отдельный вывод с именем name, подключённый к пину ZIF n. |
bus(name, n1, n2, ...) | Объявляет шину с именем name. Первый пин — младший бит, последний — старший. |
Пример:
pin('~CS', 11) -- отдельный вывод
bus('D', 27, 28, 1, 2, 5, 6, 7, 8) -- 8-битная шина данных
bus('A', 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 1, 2, 3, 4, 5) -- 16-битная шина адреса
| Функция | Назначение |
|---|---|
configInput(...) | Настраивает перечисленные выводы/шины как входы (тестер читает). |
configOutput(...) | Настраивает перечисленные выводы/шины как выходы (тестер управляет). |
Можно передавать как отдельные имена, так и целые шины:
configOutput('~CS', '~RD', '~WR', 'A0')
configInput('D', 'INT', 'CAS')
| Функция | Назначение |
|---|---|
set(...) | Устанавливает высокий уровень (логическая 1). |
clr(...) | Устанавливает низкий уровень (логический 0). |
get(name) | Возвращает текущее состояние входа (0 или 1). |
Пример:
set('~CS')
clr('~RD')
val = get('INT')
| Функция | Назначение |
|---|---|
pulseUp(name) | Формирует импульс: низкий → высокий. |
pulseDown(name) | Формирует импульс: высокий → низкий. |
Пример тактирования:
function clk(n)
for i = 1, n do
pulseUp('CLK')
end
end
| Функция | Назначение |
|---|---|
readBus(name) | Читает значение с шины (возвращает число). |
writeBus(name, value) | Записывает значение на шину. |
Пример:
d = readBus('D')
writeBus('D', 0x55)
| Функция | Назначение |
|---|---|
enablePower(...) | Подаёт питание. Принимает произвольное количество аргументов. Если число отрицательное — на соответствующий пин подаётся «земля» (GND). Если положительное — +5 В (VCC). |
disablePower() | Отключает питание. |
Пример:
enablePower(-4, 26) -- GND на пин 4, VCC на пин 26
| Функция | Назначение |
|---|---|
print(...) | Выводит текст в консоль. |
printf(format, ...) | Форматированный вывод (аналог printf в C). |
printChip() | Рисует текущее состояние всех выводов микросхемы. |
autoPrint(true/false) | Включает/выключает автоматический вывод состояния чипа в терминал после каждой операции (set / clr / pulse). |
Скорость. Утилита ctc обменивается с тестером по последовательному интерфейсу (USB-UART).
Это означает, что каждая команда скрипта — установка уровня, чтение шины, формирование импульса — превращается в отдельный запрос к тестеру,
который передаётся по медленному последовательному каналу. Встроенные тесты работают иначе: они выполняются целиком внутри тестера,
и обмен по USB нужен только для запуска и получения результата. В Lua-скрипте же весь «диалог» идёт через USB, поэтому сложные тесты
с большим количеством операций занимают заметно больше времени. Это принципиальный недостаток Lua-скриптов.
Ошибки. При ошибке в скрипте выполнение прерывается, выход в консоль.
Интерактивный режим. В REPL (ctc lua) доступно автодополнение по клавише TAB.
Это удобно для отладки и изучения доступных функций.
REPL (Read-Eval-Print Loop) — это интерактивный режим, в котором можно вводить команды по одной и сразу видеть результат. Название расшифровывается как «прочитать — вычислить — напечатать — повторить»: утилита читает введённую строку, выполняет её, выводит результат и ждёт следующую команду.
В ctc REPL запускается командой:
ctc lua
После этого можно вводить любые команды Lua по одной:
> setChipSize(28)
> pin('CLK', 20)
> set('CLK')
> get('CLK')
1
Выход из REPL — Ctrl-D.
Можно сразу загрузить скрипт и продолжить работу в REPL:
ctc lua power.lua
Это удобно, когда нужно выполнить подготовительную часть (объявление выводов, подача питания), а затем вручную подавать сигналы и смотреть на реакцию микросхемы. В REPL работает автодополнение по клавише TAB — можно набрать начало имени функции и нажать TAB, чтобы увидеть возможные варианты.
Скрипт обычно строится по такой схеме:
setChipSize, disable, объявление шин и выводов.configInput / configOutput.enablePower.clk, reset, readData, writeControl и т.д.test_rx, test_tx, testIrq и т.д.disablePower в конце.При отладке удобно кэшировать значения, чтобы не читать их повторно:
function MREQ()
if mreq_lvl < 0 then
mreq_lvl = get('~MREQ')
end
return mreq_lvl == 0
end
function clenaupCache()
mreq_lvl = -1
...
end
function check(e, msg)
if not e then
if msg ~= nil then
print('ERROR ' .. msg)
else
print('ERROR')
end
end
end
printf('READ DATA: 0x%x\n', readData())
print('------ SET DSR ------')
autoPrint(true)
printChip()
Lua-скрипты в ctc — это гибкий инструмент для написания собственных тестов и отладки микросхем.
Основной недостаток — скорость работы через USB-UART, но для многих задач (особенно для ретро-микросхем с невысокими частотами)
этого достаточно.
REPL (ctc lua) позволяет изучать доступные функции в интерактивном режиме.
Скрипт для проверки микросхемы 8251A — универсального синхронно-асинхронного приёмопередатчика.
Объявление:
setChipSize(28)
bus('D', 27, 28, 1, 2, 5, 6, 7, 8)
pin('RESET', 21) -- IN Сброс
pin('CLK', 20) -- IN Синхронизация
pin('C/~D', 12) -- IN Управление/данные
pin('~RD', 13) -- IN Чтение
pin('~WR', 10) -- IN Запись
pin('~CS', 11) -- IN Выбор микросхемы
pin('~DSR', 22) -- IN Готовность внешнего устройства передать данные
pin('~DTR', 24) -- OUT Запрос передатчика
pin('~CTS', 17) -- IN Готовность внешнего устройства принять данные
pin('~RTS', 23) -- OUT Запрос приёмника
pin('~RxC', 25) -- IN Синхронизация приёмника
pin('RxRDY', 14) -- OUT Готовность приёмника
pin('RxD', 3) -- IN Вход приёмника
pin('TxC', 9) -- IN Синхронизация передачи
pin('TxEND', 18) -- OUT Конец передачи
pin('TxRDY', 15) -- OUT Готовность передатчика
pin('TxD', 19) -- OUT Выход передатчика
pin('SYNDET', 16) -- I/O Синхронный режим: вход; асинхронный: выход
Настройка и питание:
configOutput('RESET', 'CLK', 'C/~D', '~CS', '~RD', '~WR', '~DSR', '~CTS', 'TxC', 'RxD', '~RxC')
configInput('D', '~DTR', '~RTS', 'TxD', 'TxRDY', 'TxEND', 'RxRDY', 'SYNDET')
enablePower(-4, 26)
Константы режимов:
MODE_SYNC = 0
MODE_ASYNC_1_1 = 1
MODE_ASYNC_1_16 = 2
MODE_ASYNC_1_64 = 3
WORD_LEN_5 = 0
WORD_LEN_6 = 4
WORD_LEN_7 = 8
WORD_LEN_8 = 12
PARITY_NONE = 0
PARITY_ODD = (16+32)
PARITY_EVEN = (16)
STOP_BIT_LEN_2 = (128+64)
STOP_BIT_LEN_1_5 = 128
STOP_BIT_LEN_1 = 64
SYNC_EXTERNAL = 64
SYNC_INTERNAL = 0
SYNC_CHAR_1 = 128
SYNC_CHAR_2 = 0
Команды:
CMD_TX_ENABLED = 1
CMD_TX_DISABLED = 0
CMD_TX_REQUEST = 2
CMD_RX_ENABLED = 4
CMD_RX_DISABLED = 0
CMD_PAUSE = 8
CMD_RESET_TRIGGERS = 16
CMD_RX_REQUEST = 32
CMD_RESET = 64
CMD_SYNC_WAIT = 128
Функции чтения/записи:
function clk(n)
for i = 1, n do
pulseUp('CLK')
end
end
function reset()
set('RESET')
clk(32)
clk(32)
clr('RESET')
end
function readData()
clr('C/~D')
clr('~CS')
clr('~RD')
res = readBus('D')
set('~RD')
set('~CS')
return res
end
function readStatus()
set('C/~D')
clr('~CS')
clr('~RD')
res = readBus('D')
set('~RD')
set('~CS')
return res
end
function writeData(d)
configOutput('D')
clr('C/~D')
clr('~CS')
writeBus('D', d)
clr('~WR')
set('~WR')
set('~CS')
configInput('D')
writeBus('D', 0)
end
function writeControl(d)
configOutput('D')
set('C/~D')
clr('~CS')
writeBus('D', d)
clr('~WR')
set('~WR')
set('~CS')
configInput('D')
writeBus('D', 0)
end
Пример теста приёма:
function test_rx()
writeControl(MODE_SYNC + WORD_LEN_8 + PARITY_NONE + SYNC_INTERNAL + SYNC_CHAR_2)
writeControl(0xAA)
writeControl(0x55)
writeControl(CMD_TX_ENABLED + CMD_TX_REQUEST + CMD_RX_ENABLED + CMD_RESET_TRIGGERS)
showStatus()
val = 0xAA
clk(8)
for bit = 0, 7 do
if val & (1 << bit) ~= 0 then
set('RxD')
else
clr('RxD')
end
clr('~RxC')
clk(16)
set('~RxC')
clk(16)
rxrdy = get('RxRDY')
printf('%i: RxRDY %i\n\n', bit, rxrdy)
if rxrdy == 1 then
break
end
end
printChip()
printf('READ DATA: 0x%x\n', readData())
printChip()
end
Объявление:
setChipSize(28)
disable()
bus('D', 11, 10, 9, 8, 7, 6, 5, 4)
bus('IRQ', 18, 19, 20, 21, 22, 23, 24, 25)
bus('CAS', 12, 13, 15)
pin('~CS', 1)
pin('~WR', 2)
pin('~RD', 3)
pin('~INTA', 26)
pin('~MS/SV/DE', 16)
pin('A0', 27)
pin('INT', 17)
configOutput('~CS', '~RD', '~WR', 'A0', '~INTA', '~MS/SV/DE', 'IRQ')
configInput('D', 'INT', 'CAS')
enablePower(-14, 28)
Инициализация 8259:
MODE_SINGLE = 2
MODE_MULTIPLE = 0
MODE_STEP_4 = 1 << 2
MODE_STEP_8 = 0
MODE_A = 1
MODE_SYNC = 0
MODE_ASYNC = 1 << 3
OPTION_8086 = 1
OPTION_8080 = 0
OPTION_AUTO_EOI = 1 << 1
function setup(addr, mode, icw3, icw4)
icw1 = (addr & 0xE0) | (1<<4) | mode
icw2 = (addr >> 8) & 0xff
configOutput('D')
writeData(0, icw1)
writeData(1, icw2)
if mode & 2 == 0 then
writeData(1, icw3)
end
if mode & 1 == 1 then
writeData(1, icw4)
end
configInput('D')
clr('D')
end
Проверка прерываний:
function testIrq(addr, irq, step, masterMask, slaveNum, casFromMaster)
set('IRQ' .. irq)
check(get('INT') == 1)
clr('~INTA')
clr('IRQ' .. irq)
if slaveNum == nil then
check(readBus('D') == 0xCD, 'testIrq - CALL')
else
check(readBus('D') == 0, 'testIrq - noCALL')
end
set('~INTA')
addrLo = (addr & 0xE0) + irq * step
addrHi = addr >> 8
...
end
Скрипт для проверки Z80 с эмуляцией памяти.
Объявление:
setChipSize(40)
disable()
bus('A', 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 1, 2, 3, 4, 5)
bus('D', 14, 15, 12, 8, 7, 9, 10, 13)
pin('CLK', 6)
pin('~WAIT', 24)
pin('~INT', 16)
pin('~NMI', 17)
pin('~RESET', 26)
pin('~BUSREQ', 25)
pin('~HALT', 18)
pin('~MREQ', 19)
pin('~IOREQ', 20)
pin('~RD', 21)
pin('~WR', 22)
pin('~BUSAK', 23)
pin('~M1', 27)
pin('~RFSH', 28)
configOutput('CLK', '~WAIT', '~INT', '~NMI', '~RESET', '~BUSREQ')
configInput('A', 'D', '~HALT', '~MREQ', '~IOREQ', '~RD', '~WR', '~BUSAK', '~M1', '~RFSH')
enablePower(-29, 11)
Эмуляция памяти:
ram = {
0x26, 0x10, -- ld H, 0x10
0x2E, 0x00, -- ld L, 0x00
0x7E, -- ld A, (HL)
0x77, -- ld (HL), A
0x2C, -- inc L
0xC3, 0x04, 0x00 -- jp 4
}
Обработка тактов:
function handle(cycle)
if clocl_lvl == 0 then
set('CLK')
clocl_lvl = 1
else
clr('CLK')
clocl_lvl = 0
end
printChip()
clenaupCache()
if RD() then
if data_bus_input then
configOutput('D')
data_bus_input = false
end
else
if not data_bus_input then
configInput('D')
writeBus('D', 0)
data_bus_write = 0
data_bus_input = true
end
end
if MREQ() and RD() then
d = 0
if ADDR() < #ram then
d = ram[ADDR() + 1]
else
d = d_w_t
end
if data_bus_write ~= d then
writeBus('D', d)
data_bus_write = d
end
end
...
end
Ниже можно скачать несколько файлов с примерами набросков тестов.
8051.lua
8255.lua
z80.lua