|
nedoPC.orgElectronics hobbyists community established in 2002 |
|
3niti alpha simu1 - троичный компьютер на двоичном пике
Author |
Message |
Mac Buster
Retired
Joined: 03 Aug 2003 22:37 Posts: 1474 Location: Moscow
|
Где тут очередь? Я первый?
_________________ Extreme Entertainment
|
29 Oct 2011 12:29 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
|
29 Oct 2011 15:17 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Собрал первую Rev.B:
И оно с ходу заработало:
P.S. Снял трёхчасовое с хвостиком видео сборки - ускорю в 10 раз и выложу на ютюб...
|
07 Nov 2011 23:24 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
код троичного инвертирования (он просто переставляет местами чётные и нечётные биты):
код снятия состояние переключателей и высвечивания этого состояния на светодиодах:
P.S. вышеприведённый код работает не с представлением трита как пары битов с состояниями 01,00,10 а с состояниями 10,11,01 (т.е. двоично инвертированными)
P.P.S. подпрограмма tri_neg может корректно троично инвертировать как двоично-инвертированную триаду, так и "нормальную"
Last edited by Shaos on 19 Nov 2011 09:16, edited 4 times in total.
|
16 Nov 2011 19:56 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Ниже представлен код троичного инкремента триады из второго теста. Триада - это три трита, каждый из которых представлен двумя битами - младшим битом минуса и старшим битом плюса. Триады у нас перевёрнуты из-за особенностей схемы "3niti alpha simu1", т.е. в старших битах находится младший трит, а в младших - старший:
bit 0 - установлен в "1", если старший трит триады отрицателен
bit 1 - установлен в "1", если старший трит триады положителен
bit 2 - установлен в "1", если средней трит триады отрицателен
bit 3 - установлен в "1", если средней трит триады положителен
bit 4 - установлен в "1", если младшей трит триады отрицателен
bit 5 - установлен в "1", если младшей трит триады положителен
bit 6 - должен быть нулевым
bit 7 - должен быть нулевым
| | | | Code: ; Ternary increment of W (used tmp1), C if overflow tri_inc: bcf STATUS,C ; обнуляем флаг переноса C (это у нас признак переполнения при возврате управления из подпрограммы) movwf tmp1 ; сохраняем инкрементируемую триаду в переменной tmp1
; инкремент младшего трита:
tri_i5: btfss tmp1,5 ; проверяем взведён ли бит 5 в нашей триаде (это плюс младшего трита) goto tri_i4 ; если взведён, то пропускаем переход на tri_i4 и идём дальше bcf tmp1,5 ; обнуляем бит 5 (бит плюса младшей триады) bsf tmp1,4 ; взводим бит 4 (бит минуса младшей триады) goto tri_i3 ; в результате получаем -1 вместо +1 в младшей триаде и идём на инкремент следующего трита (перенос)
tri_i4: btfss tmp1,4 ; проверяем взведён ли бит 4 в нашей триаде (это минус младшего трита) bsf tmp1,5 ; если не взведён (т.е. младший трит хранил 0), то устанавливаем бит 5 (плюс младшего трита) bcf tmp1,4 ; обнуляем бит 4 (сбрасываем минус младшего трита, если он был) movf tmp1,w ; копируем получившееся представление триады в аккумулятор W return ; возвращаем управление из подпрограммы
; инкремент среднего трита:
tri_i3: btfss tmp1,3 ; проверяем взведён ли бит 3 в нашей триаде (это плюс среднего трита) goto tri_i2 ; если взведён, то пропускаем переход на tri_i2 и идём дальше bcf tmp1,3 ; обнуляем бит 3 (бит плюса средней триады) bsf tmp1,2 ; взводим бит 2 (бит минуса средней триады) goto tri_i1 ; в результате получаем -1 вместо +1 в средней триаде и идём на инкремент следующего трита (перенос) tri_i2: btfss tmp1,2 ; проверяем взведён ли бит 2 в нашей триаде (это минус среднего трита) bsf tmp1,3 ; если не взведён (т.е. средний трит хранил 0), то устанавливаем бит 3 (плюс среднего трита) bcf tmp1,2 ; обнуляем бит 2 (сбрасываем минус среднего трита, если он был) movf tmp1,w ; копируем получившееся представление триады в аккумулятор W return ; возвращаем управление из подпрограммы
; инкремент старшего трита:
tri_i1: btfss tmp1,1 ; проверяем взведён ли бит 1 в нашей триаде (это плюс старшего трита) goto tri_i0 ; если взведён, то пропускаем переход на tri_i0 и идём дальше bcf tmp1,1 ; обнуляем бит 1 (бит плюса старшей триады) bsf tmp1,0 ; взводим бит 0 (бит минуса старшей триады) goto tri_ic ; в результате получаем -1 вместо +1 в старшей триаде и идём на взведение флага переноса (переполнение)
tri_i0: btfss tmp1,0 ; проверяем взведён ли бит 0 в нашей триаде (это минус старшего трита) bsf tmp1,1 ; если не взведён (т.е. старший трит хранил 0), то устанавливаем бит 1 (плюс старшего трита) bcf tmp1,0 ; обнуляем бит 0 (сбрасываем минус старшего трита, если он был) movf tmp1,w ; копируем получившееся представление триады в аккумулятор W return ; возвращаем управление из подпрограммы
; обработка переполнения:
tri_ic: movf tmp1,w ; копируем получившееся представление триады в аккумулятор W bsf STATUS,C ; взводим флаг C (признак переполнения) return ; возвращаем управление из подпрограммы
| | | | |
|
17 Nov 2011 21:03 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Рассмотрим принципиальную схему и печатную плату - там видно, что при положении переключателя "вверх" (плюс), контакт 4 подключен к 3, что передаёт сканирующий ноль порта A на чётные биты порта B, при положении "вниз" (минус) - контакт 1 подключен к 3, что задействует нечётные биты порта B, а при среднем положении (ноль) никуда не подключенный контакт 2 идёт на 3, что оставляет на обоих битах высокий уровень (т.к. на порту B включены внутренние подтягивающие резисторы), т.е.:
Как видно, чтобы превратить это представление в двоично закодированную троичность (binary coded ternary), нужно заменить пару 11 на пару 00: | | | | Code: ; Fix triad W (used tmp1) tri_fix: ; 15-24 cycles with call movwf tmp1 tri_f2: ; старший трит btfss tmp1,0 ; проверить что бит 0 взведён goto tri_f1 ; если нет, то идём на следующую пару бит btfss tmp1,1 ; проверить что бит 1 взведён goto tri_f1 ; если нет, то идём на следующую пару бит bcf tmp1,0 ; сбросить бит 0 bcf tmp1,1 ; сбросить бит 1 tri_f1: ; средний трит btfss tmp1,2 ; проверить что бит 2 взведён goto tri_f0 ; если нет, то идём на следующую пару бит btfss tmp1,3 ; проверить что бит 3 взведён goto tri_f0; если нет, то идём на следующую пару бит bcf tmp1,2 ; сбросить бит 2 bcf tmp1,3 ; сбросить бит 3 tri_f0: ; младший трит btfss tmp1,4 ; проверить что бит 4 взведён goto tri_fe ; если нет, то делаем возврат из подпрограммы btfss tmp1,5 ; проверить что бит 5 взведён goto tri_fe ; если нет, то делаем возврат из подпрограммы bcf tmp1,4 ; сбросить бит 4 bcf tmp1,5 ; сбросить бит 5 tri_fe: ; возвращаем скорректированую триаду обратно в W movf tmp1,w return ; возврат из подпрограммы
| | | | |
Как видно бинарное инвертирование (comf var,w) и затем троичное инвертирование (call tri_neg) будет работать быстрее (как собственно и сделано во втором тесте с часами). В случае светодиодов ноль в чётном бите подсвечивает красный светодиод (минус), а в нечётном - зелёный (плюс). А чтобы двойной светодиод не горел, оба бита должны содержать "1" (в случае обоих нулей светодиоды дадут жёлтый свет): Значит чтобы отобразить триаду на светодиодах её достаточно бинарно инвертировать:
При этом PORTA дожен держать низкий уровень в одном из пяти рядов (через RA0,RA1,RA2,RA3,RA4), соответствующих тройкам светодиодов, которую мы собираемся подсветить.
Last edited by Shaos on 19 Nov 2011 14:59, edited 1 time in total.
|
18 Nov 2011 18:36 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Если таким байтом адресоваться к 27-триадному блоку данных, то в двоичной памяти оно расположится так:
Как можно видеть, не используются любые ячейки, в адресе которых встречаются две пары единиц у любой из триад (запрещённая комбинация в binary coded ternary).
В первой версии эмулятора для платы "3niti alpha simu1" предлагается держать 27-триад кода в EEPROM (как бы троичный ROM) с вышеуказанными адресами и 27-триад данных в регистровой памяти (как бы троичный RAM) с вышеуказанными адресами, к которым прибавлено число 0x40, т.е. диапазон переменных 0x40...0x6F (нулевой банк файла региcтров в пике).
В терминах длинных адресов "3niti alpha", этот 27-триадный ROM будет отображаться на область OOOOOOxxx, а 27-триадный RAM - на область OPNNNNxxx (начиная с адреса +1093). В текущей прошивке, над которой я сейчас работаю, внешняя память пока использоваться небудет (её можно будет добавить позже - у схемы есть возможность обновления прошивки через бутлоадер).
P.S. Глядя на этот маппинг троичных адресов на двоичные у меня возникла идея - что если прямо так и хранить во внешней памяти? Можно читать по 48 байт (тогда потеряно будет 44% памяти), убрав последние 5 байт получим 43 байта (37% потерь) или даже брать блоками по 11 байт три раза, где будет всего 2 однобайтных дырки (18% потерь). Проблема лишь в том, что придётся адресоваться к памяти через умножение - надо будет умножать на 48 (00110000), 43 (00101011) или 11 (00001011) - хотя точнее надо бы сказать 33 (00100001), т.к. 3 раза по 11. Формула уможения на 48: (x<<5)+(x<<4). Формула умножения на 43: (x<<5)+(x<<3)+(x<<2)+x. Формула умножения на 11: (x<<3)+(x<<1)+x или (x<<3)+(x<<2)-x (т.е. 12*x-x). Формула умножения на 33: (x<<5)+x. Хотя блоками по 27 тоже умножать надо, причём на некрасивое число 00011011...
P.P.S. Прикинул, что при 3x11-байтовом представлении 27-триадной страницы теоретически будет читаться-писаться по 300 страниц в секунду при 100кГц или 1200 страниц при 400кГц. Но т.к. при записи будет до 10 мс задержка на запись очередной порции (до 32 байт длиной) - то это уменьшит скорость записи до 30 страниц в секунду при 100кГц или 120 страниц при 400 кГц. Напомню, что первоначально оговоренный размер блоков RAM и ROM у 3niti alpha составляет (1093*2+1)/27=81 страница, которые можно прочитать меньше чем за треть секунды и записать за 2.7 или за 1.35 секунды в зависимости от тактовой частоты ПЗУ. Байты "потерянные" при такой оптимизации можно использовать для хранения адреса страницы (для проверки корректности места чтения) и её контрольной суммы (для проверки целостности данных).
P.P.P.S. Внимательно прочитал даташит на I2C память - страничная запись подразумевает запись в 32-байтовые области с фиксированными адресами - т.е. придётся таки писать ровно по 32 байта...
|
19 Nov 2011 13:38 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Троичный инкремент 9-тритового регистра PC (program counter):
Печать триады через функцию печати в терминал из бутлоадера (макрос _serial_send_): Вот код циклического инкремента PC с печатью и результат его работы:
OOOOOOOOP
OOOOOOOPN
OOOOOOOPO
OOOOOOOPP
OOOOOOPNN
OOOOOOPNO
OOOOOOPNP
OOOOOOPON
OOOOOOPOO
OOOOOOPOP
OOOOOOPPN
OOOOOOPPO
OOOOOOPPP
OOOOOPNNN
OOOOOPNNO
OOOOOPNNP
OOOOOPNON
OOOOOPNOO
OOOOOPNOP
OOOOOPNPN
OOOOOPNPO
OOOOOPNPP
OOOOOPONN
OOOOOPONO
OOOOOPONP
OOOOOPOON
OOOOOPOOO
OOOOOPOOP
OOOOOPOPN
OOOOOPOPO
OOOOOPOPP
OOOOOPPNN
OOOOOPPNO
OOOOOPPNP
OOOOOPPON
OOOOOPPOO
OOOOOPPOP
OOOOOPPPN
OOOOOPPPO
OOOOOPPPP
OOOOPNNNN
OOOOPNNNO
и т.д.
P.S. Замерил без печати - PC полностью пробегает свой диапазон (19683) за 0.104 секунды (точнее это было 64 пробега за 6.66 секунд) - что означает примерно 189000 9-тритовых инкрементов в секунду - это и есть предельная скорость работы нашего эмулируемого процессора "3niti alpha" - 189000 операций в секунду...
P.P.S. Хотя если инкремент триады сделать не процедурным, а табличным, то будет побыстрее...
|
21 Nov 2011 00:48 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Как ни странно, но табличный метод оказался даже чуть медленнее (примерно 174000 9-тритовых инкрементов в секунду):
|
21 Nov 2011 16:30 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Самая сложная подпрограмма, необходимая для работы эмулятора "3niti alpha" - сложение двух триад с учётом флага переноса-заёма - реализация частично табличная (для сложения трёх троичных цифр), частично процедурная (для сложения триад):
| | | | Code: tri_add_3trits: ; 6 cycles with call addwf PCL,f ; jump to proper value retlw b'00000000' ; 000000 -> 0+0+0= 0 S=O C=O retlw b'00000001' ; 000001 -> 0+0-1=-1 S=N C=O retlw b'00000010' ; 000010 -> 0+0+1= 1 S=P C=O retlw b'00010000' ; 000011 -> invalid retlw b'00000001' ; 000100 -> 0-1+0=-1 S=N C=O retlw b'00000110' ; 000101 -> 0-1-1=-2 S=P C=N retlw b'00000000' ; 000110 -> 0-1+1= 0 S=O C=O retlw b'00010000' ; 000111 -> invalid retlw b'00000010' ; 001000 -> 0+1+0= 1 S=P C=O retlw b'00000000' ; 001001 -> 0+1-1= 0 S=O C=O retlw b'00001001' ; 001010 -> 0+1+1= 2 S=N C=P retlw b'00010000' ; 001011 -> invalid retlw b'00010000' ; 001100 -> invalid retlw b'00010000' ; 001101 -> invalid retlw b'00010000' ; 001110 -> invalid retlw b'00010000' ; 001111 -> invalid retlw b'00000001' ; 010000 -> -1+0+0=-1 S=N C=O retlw b'00000110' ; 010001 -> -1+0-1=-2 S=P C=N retlw b'00000000' ; 010010 -> -1+0+1= 0 S=O C=O retlw b'00010000' ; 010011 -> invalid retlw b'00000110' ; 010100 -> -1-1+0=-2 S=P C=N retlw b'00000100' ; 010101 -> -1-1-1=-3 S=O C=N retlw b'00000001' ; 010110 -> -1-1+1=-1 S=N C=O retlw b'00010000' ; 010111 -> invalid retlw b'00000000' ; 011000 -> -1+1+0= 0 S=O C=O retlw b'00000001' ; 011001 -> -1+1-1=-1 S=N C=O retlw b'00000010' ; 011010 -> -1+1+1= 1 S=P C=O retlw b'00010000' ; 011011 -> invalid retlw b'00010000' ; 011100 -> invalid retlw b'00010000' ; 011101 -> invalid retlw b'00010000' ; 011110 -> invalid retlw b'00010000' ; 011111 -> invalid retlw b'00000010' ; 100000 -> 1+0+0= 1 S=P C=O retlw b'00000000' ; 100001 -> 1+0-1= 0 S=O C=O retlw b'00001001' ; 100010 -> 1+0+1= 2 S=N C=P retlw b'00010000' ; 100011 -> invalid retlw b'00000000' ; 100100 -> 1-1+0= 0 S=O C=O retlw b'00000001' ; 100101 -> 1-1-1=-1 S=N C=O retlw b'00000010' ; 100110 -> 1-1+1= 1 S=P C=O retlw b'00010000' ; 100111 -> invalid retlw b'00001001' ; 101000 -> 1+1+0= 2 S=N C=P retlw b'00000010' ; 101001 -> 1+1-1= 1 S=P C=O retlw b'00001000' ; 101010 -> 1+1+1= 3 S=O C=P retlw b'00010000' ; 101011 -> invalid retlw b'00010000' ; 101100 -> invalid retlw b'00010000' ; 101101 -> invalid retlw b'00010000' ; 101110 -> invalid retlw b'00010000' ; 101111 -> invalid retlw b'00010000' ; 110000 -> invalid retlw b'00010000' ; 110001 -> invalid retlw b'00010000' ; 110010 -> invalid retlw b'00010000' ; 110011 -> invalid retlw b'00010000' ; 110100 -> invalid retlw b'00010000' ; 110101 -> invalid retlw b'00010000' ; 110110 -> invalid retlw b'00010000' ; 110111 -> invalid retlw b'00010000' ; 111000 -> invalid retlw b'00010000' ; 111001 -> invalid retlw b'00010000' ; 111010 -> invalid retlw b'00010000' ; 111011 -> invalid retlw b'00010000' ; 111100 -> invalid retlw b'00010000' ; 111101 -> invalid retlw b'00010000' ; 111110 -> invalid retlw b'00010000' ; 111111 -> invalid
tri_add_test MACRO B movlw B call tri_add_3trits ENDM
; Ternary adder W+A_reg+BCF=A_reg and BCF (used tmp0,tmp1,tmp2,tmp3,tmp4,cnt1) tri_adder: movwf tmp0 clrf tmp4 movlw 3 movwf cnt1 movf F_reg,w andlw b'00110000' movwf tmp1 tri_add_loop: movf tmp0,w andlw b'00110000' movwf tmp2 bcf STATUS,C rrf tmp2,f rrf tmp2,w iorwf tmp1,f movf A_reg,w andlw b'00110000' movwf tmp2 swapf tmp2,w iorwf tmp1,w call tri_add_3trits movwf tmp3 andlw b'00000011' iorwf tmp4,f movf tmp3,w andlw b'00001100' movwf tmp1 rlf tmp1,f rlf tmp1,f decfsz cnt1,f goto tri_add_next movf F_reg,w andlw b'00001111' iorwf tmp1,w movwf F_reg movf tmp4,w movwf A_reg return tri_add_next: rlf tmp4,f rlf tmp4,f rlf tmp0,f rlf tmp0,f rlf A_reg,f rlf A_reg,f goto tri_add_loop
| | | | |
Сделал замеры - если эмулятор не только бежит по PC, но и постоянно складывает две триады (взятые из PC), то скорость проседает до 33000 операций в секунду! Принимаются предложения по оптимизации этого кода
P.S. Похоже проблема решается намного проще - надо всего-то навсего перевести слагаемые из троичной системы в двоичную, сложить средствами самого пика и потом вернуть результат обратно в троичную...
|
21 Nov 2011 22:14 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Вот функция процедурного декодера кода операции:
| | | | Code: ; Perform single step of the program tri_step: movlw PC_h movwf FSR call tri_read movwf C_reg call tri_inc_pc3 btfss C_reg,0 goto tri_step0 ; Nxx btfss C_reg,2 goto tri_step_0 ; NNx btfss C_reg,4 goto tri_step__0 ; NNN (-13) SAN movlw DPn_h movwf FSR movf A_reg,w call tri_write return tri_step__0: btfsc C_reg,5 goto tri_step__1 ; NNO (-12) SAO movlw DPo_h movwf FSR movf A_reg,w call tri_write return tri_step__1: ; NNP (-11) SAP movlw DPp_h movwf FSR movf A_reg,w call tri_write return tri_step_0: btfsc C_reg,3 goto tri_step_1 ; NOx btfss C_reg,4 goto tri_step_00 ; NON (-10) SAF movf A_reg,w movwf F_reg return tri_step_00: btfsc C_reg,5 goto tri_step_01 ; NOO (-9) SPCD call tri_cur_dp movf PC_h,w movwf INDF incf FSR,f movf PC_m,w movwf INDF incf FSR,f movf PC_l,w movwf INDF return tri_step_01: ; NOP (-8) SAB movf A_reg,w movwf B_reg return tri_step_1: ; NPx btfss C_reg,4 goto tri_step_10 ; NPN (-7) SAL call tri_cur_dp goto tri_step_sal tri_step_10: btfsc C_reg,5 goto tri_step_11 ; NPO (-6) SAM call tri_cur_dp goto tri_step_sam tri_step_11: ; NPP (-5) SAH call tri_cur_dp goto tri_step_sah tri_step_sal: incf FSR,f tri_step_sam: incf FSR,f tri_step_sah: movf A_reg,w movwf INDF return tri_step0: btfsc C_reg,1 goto tri_step1 ; Oxx btfss C_reg,2 goto tri_step00 ; ONx btfss C_reg,4 goto tri_step0_0 ; ONN (-4) RLA bcf STATUS,C rlf A_reg,f rlf A_reg,f btfsc F_reg,5 bsf A_reg,1 btfsc F_reg,4 bsf A_reg,0 bcf F_reg,5 btfsc A_reg,7 bsf F_reg,5 bcf A_reg,7 bcf F_reg,4 btfsc A_reg,6 bsf F_reg,4 bcf A_reg,6 call tri_rsf return tri_step0_0: btfsc C_reg,5 goto tri_step0_1 ; ONO (-3) ADD movf B_reg,W call tri_adder call tri_rsf return tri_step0_1: ; ONP (-2) RRA btfsc F_reg,5 bsf A_reg,7 btfsc F_reg,4 bsf A_reg,6 bcf STATUS,C rrf A_reg,f btfsc STATUS,C bsf F_reg,4 bcf STATUS,C rrf A_reg,f btfsc STATUS,C bsf F_reg,5 call tri_rsf return tri_step00: btfsc C_reg,3 goto tri_step01 ; OOx btfss C_reg,4 goto tri_step000 ; OON (-1) LAI # call tri_read1pc movf tmp0,w movwf A_reg return tri_step000: btfsc C_reg,5 goto tri_step001 ; OOO (0) ADI # call tri_read1pc movf tmp0,w call tri_adder call tri_rsf return tri_step001: ; OOP (1) OPA # call tri_read1pc call tri_opa movf tmp1,w movwf A_reg call tri_rsf return tri_step01: ; OPx btfss C_reg,4 goto tri_step010 ; OPN (2) LDI # # # call tri_read3pc call tri_cur_dp movf tmp1,w movwf INDF incf FSR,f movf tmp2,w movwf INDF incf FSR,f movf tmp3,w movwf INDF return tri_step010: btfsc C_reg,5 goto tri_step011 ; OPO (3) JMP # # # call tri_read3pc movlw PC_h movwf FSR movf tmp1,w movwf INDF incf FSR,f movf tmp2,w movwf INDF incf FSR,f movf tmp3,w movwf INDF return tri_step011: ; OPP (4) OPB # # # call tri_read3pc call tri_opb movf tmp0,w movwf A_reg call tri_rsf return tri_step1: ; Pxx btfss C_reg,2 goto tri_step10 ; PNx btfss C_reg,4 goto tri_step1_0 ; PNN (5) LAN movlw DPn_h movwf FSR call tri_read movwf A_reg return tri_step1_0: btfsc C_reg,5 goto tri_step1_1 ; PNO (6) LAO movlw DPo_h movwf FSR call tri_read movwf A_reg return tri_step1_1: ; PNP (7) LAP movlw DPp_h movwf FSR call tri_read movwf A_reg return tri_step10: btfsc C_reg,3 goto tri_step11 ; POx btfss C_reg,4 goto tri_step100 ; PON (8) LAF movf F_reg,w movwf A_reg return tri_step100: btfsc C_reg,5 goto tri_step101 ; POO (9) LPCD call tri_cur_dp movf INDF,w movwf PC_h incf FSR,f movf INDF,w movwf PC_m incf FSR,f movf INDF,w movwf PC_l return tri_step101: ; POP (10) LAB movf B_reg,w movwf A_reg return tri_step11: ; NPx btfss C_reg,4 goto tri_step110 ; PPN (11) LAL call tri_cur_dp goto tri_step_lal tri_step110: btfsc C_reg,5 goto tri_step111 ; PPO (12) LAM call tri_cur_dp goto tri_step_lam tri_step111: ; PPP (13) LAH call tri_cur_dp goto tri_step_lah tri_step_lal: incf FSR,f tri_step_lam: incf FSR,f tri_step_lah: movf INDF,w movwf A_reg return
| | | | |
Сделал предварительные замеры - простейшие команды типа SAB выполняются порядка 82000 раз в секунду (примерно 12 мкс), чуть посложнее типа OPA или ADD - порядка 27000 раз в секунду (примерно 37 мкс), совсем сложные типа ADI - порядка 21000 раз в секунду (примерно 48 мкс). Это если запускать программу в пределах одной страницы, находящейся в регистрах - если же страница сидит в EEPROM, то к примеру производительность того же ADI падает до 16500 раз в секунду (60 мкс) - это из-за того что процесс чтения из EEPROM (которых в команде ADI два) это штука нетривиальная и требует некоторых странных телодвижений, отсутствующих при обращении к регистрам...
Last edited by Shaos on 25 Nov 2011 02:41, edited 1 time in total.
|
23 Nov 2011 21:25 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Вот подпрограммы для осуществления универсальных операций OPA и OPB:
| | | | Code: ; Perform OPA command on A_reg with code tmp0 and save result in tmp1 (used cnt1) tri_opa: movlw 3 movwf cnt1 clrf tmp1 tri_opa_: btfss A_reg,0 goto tri_opa1 btfsc tmp0,0 bsf tmp1,6 btfsc tmp0,1 bsf tmp1,7 goto tri_opa_loop tri_opa1: btfss A_reg,1 goto tri_opa0 btfsc tmp0,4 bsf tmp1,6 btfsc tmp0,5 bsf tmp1,7 goto tri_opa_loop tri_opa0: btfsc tmp0,2 bsf tmp1,6 btfsc tmp0,3 bsf tmp1,7 tri_opa_loop: bcf STATUS,C rrf tmp1,f rrf tmp1,f rrf A_reg,f rrf A_reg,f decfsz cnt1,f goto tri_opa_ return
; Perform OPB command on A_reg and B_reg with code tmp1,tmp2,tmp3 and save result in tmp0 (used tmp4,cnt1) tri_opb: movf tmp1,w movwf tmp4 movf tmp3,w movwf tmp0 call tri_opa movf tmp1,w movwf tmp3 movf tmp2,w movwf tmp0 call tri_opa movf tmp1,w movwf tmp2 movf tmp4,w movwf tmp0 call tri_opa clrf tmp0 btfss B_reg,0 goto tri_opb_h1 btfsc tmp1,0 bsf tmp0,0 btfsc tmp1,1 bsf tmp0,1 goto tri_opb_m tri_opb_h1: btfsc B_reg,1 goto tri_opb_h0 btfsc tmp3,0 bsf tmp0,0 btfsc tmp3,1 bsf tmp0,1 goto tri_opb_m tri_opb_h0: btfsc tmp2,0 bsf tmp0,0 btfsc tmp2,1 bsf tmp0,1 tri_opb_m: btfss B_reg,2 goto tri_opb_m1 btfsc tmp1,2 bsf tmp0,2 btfsc tmp1,3 bsf tmp0,3 goto tri_opb_l tri_opb_m1: btfsc B_reg,3 goto tri_opb_m0 btfsc tmp3,2 bsf tmp0,2 btfsc tmp3,3 bsf tmp0,3 goto tri_opb_l tri_opb_m0: btfsc tmp2,2 bsf tmp0,2 btfsc tmp2,3 bsf tmp0,3 tri_opb_l: btfss B_reg,4 goto tri_opb_l1 btfsc tmp1,4 bsf tmp0,4 btfsc tmp1,5 bsf tmp0,5 return tri_opb_l1: btfsc B_reg,5 goto tri_opb_l0 btfsc tmp3,4 bsf tmp0,4 btfsc tmp3,5 bsf tmp0,5 return tri_opb_l0: btfsc tmp2,4 bsf tmp0,4 btfsc tmp2,5 bsf tmp0,5 return
| | | | |
в железе они реализуются значительно проще...
замеры по скорости:
OPA - 26800 раз в секунду или 37 мкс
OPB - 11600 раз в секунду или 86 мкс
|
24 Nov 2011 22:45 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Ещё дополнительные подпрограммы для работы декодера команд: | | | | Code: ; Read one triad from the address stored in 3 triads started from FSR and save result to W tri_read: ; skip middle address for now... movf INDF,w subwf CA_h,w btfsc STATUS,Z goto tri_read_eep movf INDF,w subwf DA_h,w btfsc STATUS,Z goto tri_read_ram call tri_fail tri_read_eep: incf FSR,f incf FSR,f movf INDF,w call eeprom_read _bank0 return tri_read_ram: incf FSR,f incf FSR,f movf INDF,w addlw 0x40 movwf FSR movf INDF,w return
; Write one triad W to the address stored in 3 triads started from FSR (used tmp0) tri_write: ; skip middle address for now... movwf tmp0 movf INDF,w subwf CA_h,w btfsc STATUS,Z goto tri_write_eep movf INDF,w subwf DA_h,w btfsc STATUS,Z goto tri_write_ram call tri_fail tri_write_eep: incf FSR,f incf FSR,f movf tmp0,w movwf eeprom_write_value movf INDF,w call eeprom_write return tri_write_ram: incf FSR,f incf FSR,f movf INDF,w addlw 0x40 movwf FSR movf tmp0,w movwf INDF return
; Set current DP in FSR register tri_cur_dp: btfss F_reg,2 goto tri_cur_dp0 movlw DPn_h goto tri_cur_dp_ tri_cur_dp0: btfsc F_reg,3 goto tri_cur_dp1 movlw DPo_h goto tri_cur_dp_ tri_cur_dp1: movlw DPp_h tri_cur_dp_: movwf FSR return
; Set RSF (higher trit of the register F) based of the sign of the value in the register A tri_rsf: btfsc A_reg,0 goto tri_rsf_p btfsc A_reg,1 goto tri_rsf_n btfsc A_reg,2 goto tri_rsf_p btfsc A_reg,3 goto tri_rsf_n btfsc A_reg,4 goto tri_rsf_p btfsc A_reg,5 goto tri_rsf_n tri_rsf_o: bcf F_reg,0 bcf F_reg,1 return tri_rsf_n: bsf F_reg,0 bcf F_reg,1 return tri_rsf_p: bcf F_reg,0 bsf F_reg,1 return
; Read 1 immediate triad and save it in tmp0 tri_read1pc: movlw PC_h movwf FSR call tri_read movwf tmp0 call tri_inc_pc3 return
; Read 3 immediate triads and save them in tmp1,tmp2,tmp3 tri_read3pc: movlw PC_h movwf FSR call tri_read movwf tmp1 call tri_inc_pc3 movlw PC_h movwf FSR call tri_read movwf tmp2 call tri_inc_pc3 movlw PC_h movwf FSR call tri_read movwf tmp3 call tri_inc_pc3 return
| | | | |
P.S. Полный исходник смотреть тут: https://gitlab.com/nedopc/pix/-/blob/master/16/t1_test3.asmон почти в тысячу строк получился... P.P.S. Подпрограмма tri_fail должна будет вводить систему в состояние фатальной ошибки - например путём переключения всех светодиодов в жёлтый цвет и зависания в этом состоянии...
Last edited by Shaos on 25 Nov 2011 09:22, edited 1 time in total.
|
24 Nov 2011 22:51 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Вот полная таблица длительностей выполнения каждой инструкции 3niti alpha в микросекундах из регистровой памяти (тут также можно видеть визуальное отличие длительностей):
Как видно самой быстрой командой является SAF (точное значение - 11.4 мкс или примерно 87400 раз в секунду), а самой медленной - OPB (точное значение - 86.2 мкс или примерно 11600 раз в секунду). А вот тоже самое, но при чтении из EEPROM:
Как видно однотриадные команды стали дольше примерно на 7 мкс (одно чтение из EEPROM на команду), двухтриадные - на 14 мкс (два чтения из EEPROM на команду), а четырёхтриадные - на 28 мкс (четыре чтения из EEPROM на команду). Исключение составляют лишь команды LAN,LAO,LAP, которые содержат в себе неявное второе обращение к EEPROM. Также есть странная аномалия у RLA и RRA - они удлиннились не на 7, а на 9 мкс. Ещё интересно, что из-за этого неравномерного удлиннения команда ADI стала быстрее чем LDI и JMP. Теперь самая быстрая команда SAF выполняется 18.3 мкс (примерно 54400 раз в секунду), а самая долгая OPB - 113.3 мкс (примерно 8800 раз в секунду).
P.S. Обработка состояния переключателей и подсветка светодиодов добавляют около 7 мкс к каждой команде
P.P.S. Интересно, что прошивка, выполняющая все инструкции "3niti alpha" в двух страницах памяти и обрабатывающая почти всё управление (кроме возврата в главную программу M) влезла в оставленные от бутлоадера 671 байт практически в притык...
|
25 Nov 2011 00:47 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22716 Location: Silicon Valley
|
Эпопею с выкатыванием первой версии эмулятора можно считать законченной:
http://nedopc.org/nedopc/16/t1_test3.hex (3K)
http://nedopc.org/nedopc/16/t1_test3.asm (21K)
http://nedopc.org/nedopc/16/PDBLv1.zip (54K)
Прогон команды ADI # (код OOO) по всей памяти в 19,683 триад даёт примерно 52 мкс на команду (это при том, что реально данные читаются не во всех страницах). Также удалось впихнуть в прошивку хак - если переключатели W/R и I/M оба находятся в нижнем положении, то управление передаётся на бутлоадер PDBLv1, через который можно посмотреть состояние переменных и EEPROM.
|
26 Nov 2011 02:24 |
|
|
Who is online |
Users browsing this forum: No registered users and 2 guests |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum
|
|