Автор |
Сообщение |
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Hishnik писал(а): Ух! А там обобщение ресурсов хорошее получается? При параметризации все медленно растет?
В целом-то "большой" Горыныч не имеет ограничений на последовательность потоков. Можно и все время один, и каждым тактом переключаться. Правда, при этом обнаружилась проблема с распределенной памятью, потому что при такой плотной загрузке трафика, да с коммутаторами, Spartan-6 внезапно начал демонстрировать отклонения от модели (!). Редкий случай, надо сказать, пришлось побеждать вставкой аппаратных примитивов и ручным разделением на иерархические блоки, чтобы компоненты, относящиеся к разным потокам, были явно выражены на кристалле. Так что из-за таких тонких фокусов показанная конструкция очень даже имеет право на жизнь. Для переключений на каждый такт надо делать конвейер (получится типа как в XMOS чипах), а его я пока делать не умею (надо потренироваться на однопоточном процессоре)). В принципе, счетчик потоков можно (да и вроде легко будет) заменить на таблицу переключений. Про обобщение ресурсов пока сложно сказать...для адекватной оценки надо будет что-ндь править ручками уже в HDL коде или по-честному прописывать памяти (хотя бы кода), ибо или компилятор или синтезатор (скорее он)) хитрят/оптимизируют....и есть для 16ти бит разрядности и 8ми потоков съедается 20% Циклона5, то при синтезе 8бит и 4 потока он выдал меньше 1%, что явно где-то косячок.... Оно, конечно, без гарантии, что их нет и так...варнинги лезут уже на этапе трансляции Python в HDL.... Как минимум для "быстрой" генерации скелета решения сойдет...к примеру, хочется задействовать для памятей таки распределенную память, а он явно генерит регистровую (из-за чего, собственно и 20% кристалла)
[quote="Hishnik"]Ух! А там обобщение ресурсов хорошее получается? При параметризации все медленно растет?
В целом-то "большой" Горыныч не имеет ограничений на последовательность потоков. Можно и все время один, и каждым тактом переключаться. Правда, при этом обнаружилась проблема с распределенной памятью, потому что при такой плотной загрузке трафика, да с коммутаторами, Spartan-6 внезапно начал демонстрировать отклонения от модели (!). Редкий случай, надо сказать, пришлось побеждать вставкой аппаратных примитивов и ручным разделением на иерархические блоки, чтобы компоненты, относящиеся к разным потокам, были явно выражены на кристалле. Так что из-за таких тонких фокусов показанная конструкция очень даже имеет право на жизнь.[/quote]
Для переключений на каждый такт надо делать конвейер (получится типа как в XMOS чипах), а его я пока делать не умею (надо потренироваться на однопоточном процессоре)).
В принципе, счетчик потоков можно (да и вроде легко будет) заменить на таблицу переключений.
Про обобщение ресурсов пока сложно сказать...для адекватной оценки надо будет что-ндь править ручками уже в HDL коде или по-честному прописывать памяти (хотя бы кода), ибо или компилятор или синтезатор (скорее он)) хитрят/оптимизируют....и есть для 16ти бит разрядности и 8ми потоков съедается 20% Циклона5, то при синтезе 8бит и 4 потока он выдал меньше 1%, что явно где-то косячок....
Оно, конечно, без гарантии, что их нет и так...варнинги лезут уже на этапе трансляции Python в HDL....
Как минимум для "быстрой" генерации скелета решения сойдет...к примеру, хочется задействовать для памятей таки распределенную память, а он явно генерит регистровую (из-за чего, собственно и 20% кристалла)
|
|
|
|
Добавлено: Ср мар 27, 2019 10:58 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Ух! А там обобщение ресурсов хорошее получается? При параметризации все медленно растет?
В целом-то "большой" Горыныч не имеет ограничений на последовательность потоков. Можно и все время один, и каждым тактом переключаться. Правда, при этом обнаружилась проблема с распределенной памятью, потому что при такой плотной загрузке трафика, да с коммутаторами, Spartan-6 внезапно начал демонстрировать отклонения от модели (!). Редкий случай, надо сказать, пришлось побеждать вставкой аппаратных примитивов и ручным разделением на иерархические блоки, чтобы компоненты, относящиеся к разным потокам, были явно выражены на кристалле. Так что из-за таких тонких фокусов показанная конструкция очень даже имеет право на жизнь.
Ух! А там обобщение ресурсов хорошее получается? При параметризации все медленно растет?
В целом-то "большой" Горыныч не имеет ограничений на последовательность потоков. Можно и все время один, и каждым тактом переключаться. Правда, при этом обнаружилась проблема с распределенной памятью, потому что при такой плотной загрузке трафика, да с коммутаторами, Spartan-6 внезапно начал демонстрировать отклонения от модели (!). Редкий случай, надо сказать, пришлось побеждать вставкой аппаратных примитивов и ручным разделением на иерархические блоки, чтобы компоненты, относящиеся к разным потокам, были явно выражены на кристалле. Так что из-за таких тонких фокусов показанная конструкция очень даже имеет право на жизнь.
|
|
|
|
Добавлено: Ср мар 27, 2019 10:28 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
воодушевившись примером "горыныча", конечно же захотелось заиметь своего многоглавого змея... для определенности под "горынычем" буду иметь ввиду многопоточный процессор стековой архитектуры - как верно тут отмечали - "несколько голов на одном теле" (потоков на общем пространстве кода/данных). В ходе работ по связке процессора whiteTiger с питоновским компилятором Uzh мимоходом попалась надстройка/модуль MyHDL, позволяющая описывать аппаратные модули/узлы на Python (3. Начинаем FPGA на Python _ Хабр // https://m.habr.com/ru/post/439638/ ). Для профи такое скорее всего и ни к чему, но для нубов вроде меня - почему б и не попробовать Синтаксис немного проще VHDL, да и можно логически симулировать не привлекая к этому тяжеловесные системы типа Vivado, Quartus+ModelSim. Простенькие логические схемки и последовательные автоматы вроде почти несложно описывать/симулировать/транслировать в HDL (доводилось до трансляции в VHDL и скармливанию Quartus-у с последующей заливкой в ПЛИС). Ну и попутно зрела мысль описать "горыныча" на Python....Змей на змеином языке....да еще с прицелом на змеиный компилятор для него...ну прям - ну красота же! все в едином стиле))) (да, пробивается у меня иногда юношеский максимализм из серии - Форт должен быть написан на Форте ( и исполняться на форт-процессоре, как вы уже догадались )) В итоге немного набив руки на костылях в myhdl (хз, как в ней правильно описывать иерархические решения, ибо, не все что компилируется без ошибок потом можно заставить симулироваться или транслироваться в HDL - а поэтому - наиболее устойчивые решения - "одноуровневые" решения, т.е. все в одном длинном проекте ) перешел к описанию своего "змееныша" семейства "горынычей" что удобно - параметризация - как говорится - изи: Код: bits=16 Nthread=8 RAMsize=256 ROMsize=2048 STACKsize=8 RS_BASE=64 DS_BASE=8
не придумав ничего умнее - мой Горыныч будет о четырех ногах, а именно каждый поток будет состоять из четырех стадий выполнения (как в известном мультике - "пока твой конь четырьмя ногами - раааз-дваа-трии-четыыре, мальчишка ногами - раз-два-раз-два")...ну так вот - мой Горыныч - именно как тот конь) Код: #thread states task_sw = 0 #переключение контекста get_data = 1 #чтение операндов execute = 2 #выполнение save_task = 3 #переключение на следующий поток/нить
Вход процессора - тактовый сигнал и сигнал сброса, плюс шины данных и адреса для внешних устройств (доп.сигналы - потом при желании и необходимости, сейчас всё просто попробовать и проверить). Внутри - переключатель состояний процессора, номер текущего потока, наборы указателей стеков и счетчика команд для каждого из потоков, регистры текущего контекста, память данных и программ. Код: def gor(reset, clk, dat, prt):
state=Signal(modbv(0, min=0, max=4)) thread=Signal(modbv(0, min=0, max=Nthread)) th_sp = [Signal(intbv(0)[bits:]) for i in range(Nthread)] th_rp = [Signal(intbv(0)[bits:]) for i in range(Nthread)] th_ip = [Signal(intbv(0)[bits:]) for i in range(Nthread)] sp, rp, ipreg, rt = [Signal(intbv(0)[bits:]) for i in range(4)] cmd = Signal(intbv(0)[9:]) tos, sos, tdata = [Signal(intbv(0)[bits:]) for i in range(3)] D_RAM = [Signal(intbv(0)[bits:]) for i in range(RAMsize)] C_ROM = [Signal(intbv(0)[9:]) for i in range(ROMsize)]
Счетчик состояний процессора - по сбросу стоим, иначе - просто перещелкиваемся каждый такт Код: @always(clk.posedge) def st_swt(): if reset==0: state.next = task_sw else: state.next = state + 1
Переключение контекста - считываем нужные рабочие регистры из наборов для текущего потока Код: @always_comb def st_sw(): if reset==1: if state==task_sw: sp.next=th_sp[thread] rp.next=th_rp[thread] ipreg.next=th_ip[thread]
Чтение - считываем верхние элементы стеков, текущую команду потока и текущий внешний вход Код: @always_comb def st_get(): if reset==1: if state==get_data: tos.next=D_RAM[sp] sos.next=D_RAM[sp+1] rt.next=D_RAM[rp] cmd.next=C_ROM[ipreg] tdata.next=dat
и самая нудная и длинная часть - выполнение команд. Здесь аккуратно описываются все команды... Код: @always(clk.posedge) def st_ex(): if reset==1: if state==execute: # unary if cmd == nop: D_RAM[sp].next= tos th_ip[thread].next=ipreg+1 elif cmd == noti: D_RAM[sp].next= ~tos th_ip[thread].next=ipreg+1 #alu elif cmd == add: D_RAM[sp+1].next=tos+sos th_sp[thread].next=sp+1 th_ip[thread].next=ipreg+1 elif cmd == andi: D_RAM[sp+1].next=tos&sos th_sp[thread].next=sp+1 th_ip[thread].next=ipreg+1 ....... ....... else: #reset state for i in range(Nthread): th_sp[i].next=DS_BASE+STACKsize*i th_rp[i].next=RS_BASE+STACKsize*i # а тут небольшая тестовая программка ( 5 2 + ) - сложение с пятеркой, нестареющая универовская классика))) C_ROM[0].next= lit0 C_ROM[1].next= 5+0x100 C_ROM[2].next= lit0 C_ROM[3].next= 2+0x100 C_ROM[4].next= add C_ROM[5].next= nop
Финальный шаг Змееныша - инкремент счетчика потоков Код: @always(clk.posedge) def st_sv(): # task_sw = 3 if reset==1: # get_data = 1 if state== save_task: thread.next=thread+1 else: thread.next = 0
потом прописывается местная MyHDL-ская магия: Код: return st_swt, st_sw, st_get, st_ex, st_sv
и по сути - модуль готов. Магия симуляции - определяется тестовая функция, генеруется тактовая последовательность, подается сбросовый сигнал. Результат симуляции автоматически выгружается в файл .vcd (в данном случае - test.vcd), который потом скармливается GTKWave-у Код: def test(): reset = Signal(bool(0)) clk = Signal(bool(0)) dat, prt = [Signal(intbv(0)[bits:]) for i in range(2)] test = gor(reset, clk, dat, prt)
@always(delay(10)) def gen(): clk.next = not clk
@always(delay(50)) def go(): reset.next = 1
return test, gen, go
def simulate(timesteps): tb = traceSignals(test) sim = Simulation(tb) sim.run(timesteps)
simulate(4096)
При желании и необходимости может быть транслирован в HDL файл, который потом скармливается любимой среде разработки под выбранное семейство ПЛИС Код: def convert(): reset = Signal(bool(0)) clk = Signal(bool(0)) dat, prt = [Signal(intbv(0)[bits:]) for i in range(2)] toVHDL(gor, reset, clk, dat, prt) #toVerilog(gor, reset, clk, data, prt)
convert()
Вложения: |
Комментарий к файлу: Результат симуляции - потоки бодро стартуют с одного адреса, но работа со стеками идет по разным адресам - видны стеки двух первых потоков.
2019-03-27_13-14-46.png [ 97.64 Кб | Просмотров: 27516 ]
|
воодушевившись примером "горыныча", конечно же захотелось заиметь своего многоглавого змея... для определенности под "горынычем" буду иметь ввиду многопоточный процессор стековой архитектуры - как верно тут отмечали - "несколько голов на одном теле" (потоков на общем пространстве кода/данных).
В ходе работ по связке процессора whiteTiger с питоновским компилятором Uzh мимоходом попалась надстройка/модуль MyHDL, позволяющая описывать аппаратные модули/узлы на Python (3. Начинаем FPGA на Python _ Хабр // [url]https://m.habr.com/ru/post/439638/[/url] ). Для профи такое скорее всего и ни к чему, но для нубов вроде меня - почему б и не попробовать :oops: Синтаксис немного проще VHDL, да и можно логически симулировать не привлекая к этому тяжеловесные системы типа Vivado, Quartus+ModelSim.
Простенькие логические схемки и последовательные автоматы вроде почти несложно описывать/симулировать/транслировать в HDL (доводилось до трансляции в VHDL и скармливанию Quartus-у с последующей заливкой в ПЛИС).
Ну и попутно зрела мысль описать "горыныча" на Python....Змей на змеином языке....да еще с прицелом на змеиный компилятор для него...ну прям - ну красота же! все в едином стиле))) (да, пробивается у меня иногда юношеский максимализм из серии - Форт должен быть написан на Форте ( и исполняться на форт-процессоре, как вы уже догадались 8) )) В итоге немного набив руки на костылях в myhdl (хз, как в ней правильно описывать иерархические решения, ибо, не все что компилируется без ошибок потом можно заставить симулироваться или транслироваться в HDL - а поэтому - наиболее устойчивые решения - "одноуровневые" решения, т.е. все в одном длинном проекте :D ) перешел к описанию своего "змееныша" семейства "горынычей"
что удобно - параметризация - как говорится - изи: [code]bits=16 Nthread=8 RAMsize=256 ROMsize=2048 STACKsize=8 RS_BASE=64 DS_BASE=8 [/code]
не придумав ничего умнее - мой Горыныч будет о четырех ногах, а именно каждый поток будет состоять из четырех стадий выполнения (как в известном мультике - "пока твой конь четырьмя ногами - раааз-дваа-трии-четыыре, мальчишка ногами - раз-два-раз-два")...ну так вот - мой Горыныч - именно как тот конь) [code]#thread states task_sw = 0 #переключение контекста get_data = 1 #чтение операндов execute = 2 #выполнение save_task = 3 #переключение на следующий поток/нить [/code]
Вход процессора - тактовый сигнал и сигнал сброса, плюс шины данных и адреса для внешних устройств (доп.сигналы - потом при желании и необходимости, сейчас всё просто попробовать и проверить). Внутри - переключатель состояний процессора, номер текущего потока, наборы указателей стеков и счетчика команд для каждого из потоков, регистры текущего контекста, память данных и программ. [code]def gor(reset, clk, dat, prt):
state=Signal(modbv(0, min=0, max=4)) thread=Signal(modbv(0, min=0, max=Nthread)) th_sp = [Signal(intbv(0)[bits:]) for i in range(Nthread)] th_rp = [Signal(intbv(0)[bits:]) for i in range(Nthread)] th_ip = [Signal(intbv(0)[bits:]) for i in range(Nthread)] sp, rp, ipreg, rt = [Signal(intbv(0)[bits:]) for i in range(4)] cmd = Signal(intbv(0)[9:]) tos, sos, tdata = [Signal(intbv(0)[bits:]) for i in range(3)] D_RAM = [Signal(intbv(0)[bits:]) for i in range(RAMsize)] C_ROM = [Signal(intbv(0)[9:]) for i in range(ROMsize)] [/code]
Счетчик состояний процессора - по сбросу стоим, иначе - просто перещелкиваемся каждый такт [code] @always(clk.posedge) def st_swt(): if reset==0: state.next = task_sw else: state.next = state + 1 [/code] Переключение контекста - считываем нужные рабочие регистры из наборов для текущего потока [code] @always_comb def st_sw(): if reset==1: if state==task_sw: sp.next=th_sp[thread] rp.next=th_rp[thread] ipreg.next=th_ip[thread]
[/code] Чтение - считываем верхние элементы стеков, текущую команду потока и текущий внешний вход [code] @always_comb def st_get(): if reset==1: if state==get_data: tos.next=D_RAM[sp] sos.next=D_RAM[sp+1] rt.next=D_RAM[rp] cmd.next=C_ROM[ipreg] tdata.next=dat [/code] и самая нудная и длинная часть - выполнение команд. Здесь аккуратно описываются все команды... [code] @always(clk.posedge) def st_ex(): if reset==1: if state==execute: # unary if cmd == nop: D_RAM[sp].next= tos th_ip[thread].next=ipreg+1 elif cmd == noti: D_RAM[sp].next= ~tos th_ip[thread].next=ipreg+1 #alu elif cmd == add: D_RAM[sp+1].next=tos+sos th_sp[thread].next=sp+1 th_ip[thread].next=ipreg+1 elif cmd == andi: D_RAM[sp+1].next=tos&sos th_sp[thread].next=sp+1 th_ip[thread].next=ipreg+1 ....... ....... else: #reset state for i in range(Nthread): th_sp[i].next=DS_BASE+STACKsize*i th_rp[i].next=RS_BASE+STACKsize*i # а тут небольшая тестовая программка ( 5 2 + ) - сложение с пятеркой, нестареющая универовская классика))) C_ROM[0].next= lit0 C_ROM[1].next= 5+0x100 C_ROM[2].next= lit0 C_ROM[3].next= 2+0x100 C_ROM[4].next= add C_ROM[5].next= nop
[/code]
Финальный шаг Змееныша - инкремент счетчика потоков [code] @always(clk.posedge) def st_sv(): # task_sw = 3 if reset==1: # get_data = 1 if state== save_task: thread.next=thread+1 else: thread.next = 0
[/code] потом прописывается местная MyHDL-ская магия: [code] return st_swt, st_sw, st_get, st_ex, st_sv [/code]
и по сути - модуль готов.
Магия симуляции - определяется тестовая функция, генеруется тактовая последовательность, подается сбросовый сигнал. Результат симуляции автоматически выгружается в файл .vcd (в данном случае - test.vcd), который потом скармливается GTKWave-у [code]def test(): reset = Signal(bool(0)) clk = Signal(bool(0)) dat, prt = [Signal(intbv(0)[bits:]) for i in range(2)] test = gor(reset, clk, dat, prt)
@always(delay(10)) def gen(): clk.next = not clk
@always(delay(50)) def go(): reset.next = 1
return test, gen, go
def simulate(timesteps): tb = traceSignals(test) sim = Simulation(tb) sim.run(timesteps)
simulate(4096) [/code]
При желании и необходимости может быть транслирован в HDL файл, который потом скармливается любимой среде разработки под выбранное семейство ПЛИС [code]def convert(): reset = Signal(bool(0)) clk = Signal(bool(0)) dat, prt = [Signal(intbv(0)[bits:]) for i in range(2)] toVHDL(gor, reset, clk, dat, prt) #toVerilog(gor, reset, clk, data, prt)
convert() [/code]
|
|
|
|
Добавлено: Ср мар 27, 2019 10:20 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Это больше из серии "сделать просто, давайте теперь посмотрим, что из этого получится". Технически это попросту остановка/запуск счетчика, переключающего таблицу потоков. Минус, конечно, в том, что какой-то поток может внести неопределенность в работу остальных потоков... однако он, в принципе, и так может это сделать, переписав таблицу очередности исполнения потоков. Плюс - становится возможной реализация операций, критичных к доступу к системной шине.
Это больше из серии "сделать просто, давайте теперь посмотрим, что из этого получится". Технически это попросту остановка/запуск счетчика, переключающего таблицу потоков. Минус, конечно, в том, что какой-то поток может внести неопределенность в работу остальных потоков... однако он, в принципе, и так может это сделать, переписав таблицу очередности исполнения потоков. Плюс - становится возможной реализация операций, критичных к доступу к системной шине.
|
|
|
|
Добавлено: Пн июн 19, 2017 12:18 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Hishnik писал(а): Напрашиваются простые в реализации команды LOCK и UNLOCK для блокировки/разблокировки переключения по таблице. Тогда текущий поток сможет просто остановить счетчик, забрав управление себе на некоторое количество тактов. Например, для гарантированной установки семафора. Потом UNLOCK восстановит переключение. ну вооот...притеснения начались)))
[quote="Hishnik"]Напрашиваются простые в реализации команды LOCK и UNLOCK для блокировки/разблокировки переключения по таблице. Тогда текущий поток сможет просто остановить счетчик, забрав управление себе на некоторое количество тактов. Например, для гарантированной установки семафора. Потом UNLOCK восстановит переключение.[/quote]
ну вооот...притеснения начались)))
|
|
|
|
Добавлено: Пт июн 16, 2017 12:08 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Напрашиваются простые в реализации команды LOCK и UNLOCK для блокировки/разблокировки переключения по таблице. Тогда текущий поток сможет просто остановить счетчик, забрав управление себе на некоторое количество тактов. Например, для гарантированной установки семафора. Потом UNLOCK восстановит переключение.
Напрашиваются простые в реализации команды LOCK и UNLOCK для блокировки/разблокировки переключения по таблице. Тогда текущий поток сможет просто остановить счетчик, забрав управление себе на некоторое количество тактов. Например, для гарантированной установки семафора. Потом UNLOCK восстановит переключение.
|
|
|
|
Добавлено: Чт июн 15, 2017 16:14 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
По результатам синтеза для разных ПЛИС:
Ядро на 8 потоков, с таблицей переключений на 16 ячеек.
Spartan-6 (-2 speed grade) - 54 МГц Artix-7 (-1 speed grade) - 94 МГц Kintex-7 (-1 speed grade) - 127 МГц
Критическая цепь включает таблицы, то есть заметное падение частоты относительно простых ядер имеет объективные основания. Опять же, Artix-7 в целом несущественно быстрее, чем Spartan-6, но в данном случае более развитая архитектура 7-й серии помогает строить коммутаторы более эффективно. А они в многопоточной схеме вносят существенный вклад.
По результатам синтеза для разных ПЛИС:
Ядро на 8 потоков, с таблицей переключений на 16 ячеек.
Spartan-6 (-2 speed grade) - 54 МГц Artix-7 (-1 speed grade) - 94 МГц Kintex-7 (-1 speed grade) - 127 МГц
Критическая цепь включает таблицы, то есть заметное падение частоты относительно простых ядер имеет объективные основания. Опять же, Artix-7 в целом несущественно быстрее, чем Spartan-6, но в данном случае более развитая архитектура 7-й серии помогает строить коммутаторы более эффективно. А они в многопоточной схеме вносят существенный вклад.
|
|
|
|
Добавлено: Ср дек 28, 2016 15:15 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Картинки оказались снизу. Я думал, что они вставятся в месте упоминания, но движок поиклеил их внизу основного текста.
Картинки оказались снизу. Я думал, что они вставятся в месте упоминания, но движок поиклеил их внизу основного текста.
|
|
|
|
Добавлено: Пн дек 05, 2016 14:19 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
первые две картинки не видны 8(
первые две картинки не видны 8(
|
|
|
|
Добавлено: Пн дек 05, 2016 04:47 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Итак, многопоточный Горыныч взлетел! Если раньше проверялась концепция в целом (ну и конечно же, есть проект, в который такое очень нужно), то сегодня состоялся торжественный полет многоголового дракона с тестовой задачкой. Практически бесполезная картинка с раскладкой процессора по самому маленькому кристаллу Spartan-6. Бесполезная потому, что на практике нужна для тонкой подстройки размещения компонентов, но для маленьких ПЛИС и относительно простых проектов инструмент неактуален, САПР и так справляется. Тем не менее, вид кристалла с раскладкой компонентов обычно впечатляет и выглядит приятным. [img]kf7_layout.png[/img] Вот этим оно программируется. Инструмент довольно старый, и характерен тем, что в качестве компилятора использует quark.dll, передавая ему введенные в редакторе строки. Предварительно интерпретируется файл с собственно кросс-компилятором, поэтому можно довольно оперативно менять компилятор, вводить новые команды и вообще отлаживать совместно систему процессор + компилятор, не имея существенных задержек при необходимости переключиться между одним и другим. [img]kf7_ide.png[/img] А вот код: Код: : DELAY 200000 BEGIN 1 - DUP 0 = UNTIL DROP ;
: DELAY1 100000 BEGIN 1 - DUP 0 = UNTIL DROP ;
: DELAY2 100000 BEGIN 1 - DUP 0 = UNTIL DROP ;
: DELAY3 100000 BEGIN 1 - DUP 0 = UNTIL DROP ;
VARIABLE X1 VARIABLE X2 VARIABLE X3
MAIN: // your program will start here
0 0 SETTHREAD 1 1 SETTHREAD 2 2 SETTHREAD 3 3 SETTHREAD 0 4 SETTHREAD 1 5 SETTHREAD 2 6 SETTHREAD 3 7 SETTHREAD
49 X1 ! 49 X2 ! 49 X3 !
BEGIN X1 @ -1 OUTPORT DELAY X2 @ -1 OUTPORT DELAY X3 @ -1 OUTPORT DELAY DELAY AGAIN
MAIN1: // thread 1 start BEGIN X1 @ 1 + X1 ! DELAY1 AGAIN
MAIN2: // thread 2 start BEGIN X2 @ 1 + X2 ! DELAY2 AGAIN
MAIN3: // thread 3 start BEGIN X3 @ 1 + X3 ! DELAY3 AGAIN Можно видеть, что в процессоре четыре потока. Это именно потоки, т.е. работа идет в общем адресном пространстве, что касается и кода, и данных. Команда SETTHREAD управляет очередностью потоков. В процессоре имеется таблица на 8 значений, каждая из которых хранит номер потока, который должен исполниться на i-м такте. Перебор таблицы идет строго последовательно. В примере показано, что нулевой поток настраивает исполнение на 01230123, т.е. каждому потоку выделяется 25% от процессорного времени. Ранее определенные слова доступны всем, что соответствует определению потока - они выполняются в общем адресном пространстве. Потоки 1 - 3 независимо выполняют инкремент каждый своей переменной ("своей" в данном случае условное понятие - они могут изменить что угодно), а поток 0 отправляет содержимое переменных в UART. По адресу -1 находится регистр передатчика UART. Характеристики получились вполне приятные для low-end ПЛИС. Синтезатор стабильно выдает 75-80 МГц (что характерно для 32-разрядных процессоров как таковых), при этом занято чуть больше половины от самого маленького кристалла S6LX4. Что, собственно, и планировалось. Сегмент недорогих проектов на базе ПЛИС довольно широк, но вот переходить от LX4 к LX9 и далее - это уже вызывает некоторые вопросы. Собственно, а что ж нам не дает остаться на самой дешевой ПЛИС? Однако попытка разместить в LX4 какие-то компоненты, облегчающие процессору жизнь, быстро упирается в недостаток свободного места. С другой стороны, и 70-80 МГц процессор просто не успевает быстро переключиться между задачами, даже если они медленные, а процессору хватает тех 70 МГц, которые обеспечит ПЛИС. И вот тут самое главное. Переключение потоков мгновенное. Это означает, что никаких штрафов, сохранений/восстановлений контекста и прочего не существует. Это просто четыре нити исполнения команд, которые чередуются с тактовой частотой процессора. Это, однако, не Propeller https://www.parallax.com/product/p8x32a-d40 , у которого 8 потоков, однако они представлены практически полноценными процессорными ядрами, содержащими внутри и регистры, и АЛУ, и даже по 512 слов памяти. Горыныч же полностью оправдывает свой мифологический образ - головы сидят на общем массивном теле, и на 4 потока имеется одно АЛУ, одна память команд и одна память данных. Что касается штрафов, то ситуация даже в некотором роде обратная - наличие чередования потоков маскирует штрафы на переходы (!). То есть если один поток выполняет команду JMP/CALL/RET, но следующий такт выделен другому потоку, то текущий поток в фоне "дойдет" до состояния, соответствующего нормальному продолжению работы. Т.е. схема 01230123 имеет более высокий КПД, чем 00112233, где возможны штрафы на сброс конвейера. В качестве вишенки на торте - для ядра есть и Форт, и Си. Если точнее, Си, конечно же, не на 100% совместим со стандартом, однако Си-подобный входной язык генерирует вполне эффективный код, для которого еще есть и некоторая поддержка в ядре. В частности, удалось слегка ослабить проблему с "бутылочным горлышком" стека и Си-код может свободно адресовать стеки наравне с памятью (и даже иметь стековые кадры параметров в произвольном месте).
Вложения: |
kf7_ide.png [ 21.36 Кб | Просмотров: 39566 ]
|
kf7_layout.png [ 53.54 Кб | Просмотров: 39566 ]
|
Итак, многопоточный Горыныч взлетел! Если раньше проверялась концепция в целом (ну и конечно же, есть проект, в который такое очень нужно), то сегодня состоялся торжественный полет многоголового дракона с тестовой задачкой. Практически бесполезная картинка с раскладкой процессора по самому маленькому кристаллу Spartan-6. Бесполезная потому, что на практике нужна для тонкой подстройки размещения компонентов, но для маленьких ПЛИС и относительно простых проектов инструмент неактуален, САПР и так справляется. Тем не менее, вид кристалла с раскладкой компонентов обычно впечатляет и выглядит приятным.
[img]kf7_layout.png[/img]
Вот этим оно программируется. Инструмент довольно старый, и характерен тем, что в качестве компилятора использует quark.dll, передавая ему введенные в редакторе строки. Предварительно интерпретируется файл с собственно кросс-компилятором, поэтому можно довольно оперативно менять компилятор, вводить новые команды и вообще отлаживать совместно систему процессор + компилятор, не имея существенных задержек при необходимости переключиться между одним и другим.
[img]kf7_ide.png[/img]
А вот код: [code] : DELAY 200000 BEGIN 1 - DUP 0 = UNTIL DROP ;
: DELAY1 100000 BEGIN 1 - DUP 0 = UNTIL DROP ;
: DELAY2 100000 BEGIN 1 - DUP 0 = UNTIL DROP ;
: DELAY3 100000 BEGIN 1 - DUP 0 = UNTIL DROP ;
VARIABLE X1 VARIABLE X2 VARIABLE X3
MAIN: // your program will start here
0 0 SETTHREAD 1 1 SETTHREAD 2 2 SETTHREAD 3 3 SETTHREAD 0 4 SETTHREAD 1 5 SETTHREAD 2 6 SETTHREAD 3 7 SETTHREAD
49 X1 ! 49 X2 ! 49 X3 !
BEGIN X1 @ -1 OUTPORT DELAY X2 @ -1 OUTPORT DELAY X3 @ -1 OUTPORT DELAY DELAY AGAIN
MAIN1: // thread 1 start BEGIN X1 @ 1 + X1 ! DELAY1 AGAIN
MAIN2: // thread 2 start BEGIN X2 @ 1 + X2 ! DELAY2 AGAIN
MAIN3: // thread 3 start BEGIN X3 @ 1 + X3 ! DELAY3 AGAIN [/code]
Можно видеть, что в процессоре четыре потока. Это именно потоки, т.е. работа идет в общем адресном пространстве, что касается и кода, и данных. Команда SETTHREAD управляет очередностью потоков. В процессоре имеется таблица на 8 значений, каждая из которых хранит номер потока, который должен исполниться на i-м такте. Перебор таблицы идет строго последовательно. В примере показано, что нулевой поток настраивает исполнение на 01230123, т.е. каждому потоку выделяется 25% от процессорного времени. Ранее определенные слова доступны всем, что соответствует определению потока - они выполняются в общем адресном пространстве. Потоки 1 - 3 независимо выполняют инкремент каждый своей переменной ("своей" в данном случае условное понятие - они могут изменить что угодно), а поток 0 отправляет содержимое переменных в UART. По адресу -1 находится регистр передатчика UART.
Характеристики получились вполне приятные для low-end ПЛИС. Синтезатор стабильно выдает 75-80 МГц (что характерно для 32-разрядных процессоров как таковых), при этом занято чуть больше половины от самого маленького кристалла S6LX4. Что, собственно, и планировалось. Сегмент недорогих проектов на базе ПЛИС довольно широк, но вот переходить от LX4 к LX9 и далее - это уже вызывает некоторые вопросы. Собственно, а что ж нам не дает остаться на самой дешевой ПЛИС? Однако попытка разместить в LX4 какие-то компоненты, облегчающие процессору жизнь, быстро упирается в недостаток свободного места. С другой стороны, и 70-80 МГц процессор просто не успевает быстро переключиться между задачами, даже если они медленные, а процессору хватает тех 70 МГц, которые обеспечит ПЛИС.
И вот тут самое главное. Переключение потоков мгновенное. Это означает, что никаких штрафов, сохранений/восстановлений контекста и прочего [b]не существует[/b]. Это просто четыре нити исполнения команд, которые чередуются с тактовой частотой процессора. Это, однако, не Propeller [url]https://www.parallax.com/product/p8x32a-d40[/url] , у которого 8 потоков, однако они представлены практически полноценными процессорными ядрами, содержащими внутри и регистры, и АЛУ, и даже по 512 слов памяти. Горыныч же полностью оправдывает свой мифологический образ - головы сидят на общем массивном теле, и на 4 потока имеется одно АЛУ, одна память команд и одна память данных. Что касается штрафов, то ситуация даже в некотором роде обратная - наличие чередования потоков маскирует штрафы на переходы (!). То есть если один поток выполняет команду JMP/CALL/RET, но следующий такт выделен другому потоку, то текущий поток в фоне "дойдет" до состояния, соответствующего нормальному продолжению работы. Т.е. схема 01230123 имеет более высокий КПД, чем 00112233, где возможны штрафы на сброс конвейера.
В качестве вишенки на торте - для ядра есть и Форт, и Си. Если точнее, Си, конечно же, не на 100% совместим со стандартом, однако Си-подобный входной язык генерирует вполне эффективный код, для которого еще есть и некоторая поддержка в ядре. В частности, удалось слегка ослабить проблему с "бутылочным горлышком" стека и Си-код может свободно адресовать стеки наравне с памятью (и даже иметь стековые кадры параметров в произвольном месте).
|
|
|
|
Добавлено: Пн дек 05, 2016 03:32 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Подробности примерно такие. Сами стековые софт-процессоры плавно распадаются на два направления.
Первое - добавление умеренного по скорости, но достаточно функционального ядра с компактной программой в FPGA среднего-большого объема. Основной приоритет здесь - именно компактность кода, чтобы не собирать систему из flash+DDR+BRAM cache. Вместо этого внутренняя память (BRAM) выступает в качестве основной памяти форт-процессора, который используется для управления "основной аппаратурой". Как правило, это параллельно работающие блоки цифровой обработки сигналов. В данном случае от процессора не требуется производительность, поскольку он не является основным вычислительным элементом (вместо него основную работу выполняют IP-ядра).
Второе направление, неожиданно получившее развитие в виде анонса нового семейства Spartan-7 - реализация процессорной системы в ПЛИС минимального объема. В настоящий момент это Spartan-6 LX4. В анонсе заявлен Spartan-7 LX6. Т.е. число ячеек относительно невелико, и забрав 2-3 тысячи на процессор, мы получим просто дорогой и медленный процессор, без сопутствующих преимуществ в виде какой-то специализированной периферии. А если опять-таки полагаться на то, что ПЛИС == периферия, то от процессора требуется не только уметь выполнять программу из небольшой памяти, но и самому быть небольшим.
Так вот, новая системка под рабочим названием "Змей Горыныч" представляет собой не только ядро, которое на 20-30% компактнее предыдущего, но к тому же еще и многопоточное. Таким образом, вместо одного ядра на 80 МГц (больше - можно, но сопряжено с длительными итерациями оптимизации на кристалле) можно получить 4 потока по 20 МГц. Собственно, ограничений тут нет, и очередность потоков задается таблицей. Количество потоков теоретически ограничено разумными пределами, кроме того, контекст потока представляет собой несколько регистров (счетчик команд, указатели стеков). Сейчас оно помещается в половину от LX4 и требует совсем немного памяти для работы. Конечно, пришлось решить некоторое количество схемотехнических задачек, однако главный эффект достигнут - мгновенное переключение контекста.
Подробности примерно такие. Сами стековые софт-процессоры плавно распадаются на два направления.
Первое - добавление умеренного по скорости, но достаточно функционального ядра с компактной программой в FPGA среднего-большого объема. Основной приоритет здесь - именно компактность кода, чтобы не собирать систему из flash+DDR+BRAM cache. Вместо этого внутренняя память (BRAM) выступает в качестве основной памяти форт-процессора, который используется для управления "основной аппаратурой". Как правило, это параллельно работающие блоки цифровой обработки сигналов. В данном случае от процессора не требуется производительность, поскольку он не является основным вычислительным элементом (вместо него основную работу выполняют IP-ядра).
Второе направление, неожиданно получившее развитие в виде анонса нового семейства Spartan-7 - реализация процессорной системы в ПЛИС минимального объема. В настоящий момент это Spartan-6 LX4. В анонсе заявлен Spartan-7 LX6. Т.е. число ячеек относительно невелико, и забрав 2-3 тысячи на процессор, мы получим просто дорогой и медленный процессор, без сопутствующих преимуществ в виде какой-то специализированной периферии. А если опять-таки полагаться на то, что ПЛИС == периферия, то от процессора требуется не только уметь выполнять программу из небольшой памяти, но и самому быть небольшим.
Так вот, новая системка под рабочим названием "Змей Горыныч" представляет собой не только ядро, которое на 20-30% компактнее предыдущего, но к тому же еще и многопоточное. Таким образом, вместо одного ядра на 80 МГц (больше - можно, но сопряжено с длительными итерациями оптимизации на кристалле) можно получить 4 потока по 20 МГц. Собственно, ограничений тут нет, и очередность потоков задается таблицей. Количество потоков теоретически ограничено разумными пределами, кроме того, контекст потока представляет собой несколько регистров (счетчик команд, указатели стеков). Сейчас оно помещается в половину от LX4 и требует совсем немного памяти для работы. Конечно, пришлось решить некоторое количество схемотехнических задачек, однако главный эффект достигнут - мгновенное переключение контекста.
|
|
|
|
Добавлено: Вс ноя 06, 2016 03:26 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
круто!
А есть подробности? фотки? картинки?
круто!
А есть подробности? фотки? картинки?
|
|
|
|
Добавлено: Ср ноя 02, 2016 07:37 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
Сегодня он "взлетел" Собственно, в RTL-модели и даже на Avnet Microboard он уже некоторое время мигал светодиодами, но только что состоялся успешный запуск после компилятора с отсылкой данных по UART.
Сегодня он "взлетел" :) Собственно, в RTL-модели и даже на Avnet Microboard он уже некоторое время мигал светодиодами, но только что состоялся успешный запуск после компилятора с отсылкой данных по UART.
|
|
|
|
Добавлено: Сб окт 29, 2016 22:46 |
|
|
|
|
|
Заголовок сообщения: |
Re: Форт-процессор и контекст потока |
|
|
даешь многопоточный процессор в массы!!!
даешь многопоточный процессор в массы!!!
|
|
|
|
Добавлено: Чт окт 06, 2016 18:11 |
|
|
|
|
|
Заголовок сообщения: |
Форт-процессор и контекст потока |
|
|
У форт-процессора есть интересное свойство - небольшой размер контекста потока. Поскольку роль регистров выполняет стек, при переключении на другой поток необязательно иметь полный комплект регистров общего назначения - достаточно переключить указатель стека. Если не заниматься реализацией аппаратной поддержки стека циклов и прочих вещей, контекст потока можно ограничить небольшим набором указателей - стека, стека возвратов и счетчик команд потока. Размещение всех стеков в памяти несколько снижает эффективность форт-процессора в терминах инструкций на типичные алгоритмы, однако оно компенсируется упрощением самой схемы. К тому же при общем избытке производительности (например, в небыстром embedded) и наличии нескольких "естественных" (т.е. определяемых спецификой задач) потоков вычислений компактность многопотоковой схемы будет важнее пиковой производительности, на которую все равно в embedded не ориентируются.
У форт-процессора есть интересное свойство - небольшой размер контекста потока. Поскольку роль регистров выполняет стек, при переключении на другой поток необязательно иметь полный комплект регистров общего назначения - достаточно переключить указатель стека. Если не заниматься реализацией аппаратной поддержки стека циклов и прочих вещей, контекст потока можно ограничить небольшим набором указателей - стека, стека возвратов и счетчик команд потока. Размещение всех стеков в памяти несколько снижает эффективность форт-процессора в терминах инструкций на типичные алгоритмы, однако оно компенсируется упрощением самой схемы. К тому же при общем избытке производительности (например, в небыстром embedded) и наличии нескольких "естественных" (т.е. определяемых спецификой задач) потоков вычислений компактность многопотоковой схемы будет важнее пиковой производительности, на которую все равно в embedded не ориентируются.
|
|
|
|
Добавлено: Ср окт 05, 2016 22:55 |
|
|
|
|