Lua-скрипты для тестера микросхем

Lua скрипты для тестера микросхем

Утилита 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')

Этот скрипт:

  1. Задаёт размер микросхемы (28 выводов).
  2. Объявляет шину данных D из 8 линий.
  3. Объявляет два отдельных вывода — CLK и ~RESET.
  4. Настраивает CLK и ~RESET как выходы, D — как вход.
  5. Подаёт питание: GND на пин 4, VCC на пин 26.
  6. Сбрасывает микросхему.

Запуск скрипта:

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

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, чтобы увидеть возможные варианты.

Организация скрипта

Скрипт обычно строится по такой схеме:

  1. Инициализация — setChipSize, disable, объявление шин и выводов.
  2. Настройка направлений — configInput / configOutput.
  3. Подача питания — enablePower.
  4. Константы — режимы, команды, битовые маски.
  5. Вспомогательные функции — clk, reset, readData, writeControl и т.д.
  6. Тестовые функции — test_rx, test_tx, testIrq и т.д.
  7. Запуск — вызов нужной тестовой функции, 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) позволяет изучать доступные функции в интерактивном режиме.

Примеры

Пример 1: 8251A (UART)

Скрипт для проверки микросхемы 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

Пример 2: 8259A (контроллер прерываний)

Объявление:

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

Пример 3: Z80 (микропроцессор)

Скрипт для проверки 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

Ниже можно скачать несколько файлов с примерами набросков тестов.

Файлы

Download8051.lua
Download8255.lua
Downloadz80.lua
Рейтинг: 
0
Голосов еще нет