Многоядерные процессоры с низким энергопотреблением
файл hartley_test.vf
Код:
v.VF +include" c7Gr01/romconfig.f" \ подключение функций ROM нужной версии процессора include <path>\Fpmath.f \ для вычисления таблиц функции cas() подключаем библиотеку плавающей точки компилятора SwiftForth 16 VALUE num \ =N количество отсчетов 0 VALUE v \ номер отсчет Хартли спектра include coef0.vf \ определяем слова, вычисляющие cas в формате с фиксированной точкой 10 {node \ ядро выдает 16 последовательных отсчетов сигнала 0 org here =p 'r--- # b! 1 # !b 1 # !b 1 # !b 1 # !b 0 # !b 0 # !b 0 # !b 0 # !b 1 # !b 1 # !b 1 # !b 1 # !b 0 # !b 0 # !b 0 # !b 0 # !b node} ( код для ядер практически одинаков, за исключением портов приема и передачи данных, определяем несколько переменных, позволяющих настроить порты в коде для каждого из ядер ) 0 VALUE in_ 0 VALUE out_ 0 TO v \ - номер отсчета Х 11 {node 0 org \ установка начального адреса компиляции \ задание таблицы коэффициентов cas(vt) coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики 'r--- TO in_ '--l- TO out_ include hart2.vf \ настройка переменных и переход к вычислению коэффициента H(v) \ -- hh hl '---u TO in_ '---u TO out_ include fourier2.vf \ вычисление отсчета мощностного спектра Фурье - Фр(v) \ -- fh fl node} 1 TO v 12 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики '--l- TO in_ 'r--- TO out_ include hart2.vf '---u TO in_ '---u TO out_ include fourier2.vf node} 2 TO v 13 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики 'r--- TO in_ '--l- TO out_ include hart2.vf '---u TO in_ '---u TO out_ include fourier2.vf node} 3 TO v 14 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики '--l- TO in_ 'r--- TO out_ include hart2.vf '---u TO in_ '---u TO out_ include fourier2.vf node} 4 TO v 15 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики 'r--- TO in_ '--l- TO out_ include hart2.vf '---u TO in_ '---u TO out_ include fourier2.vf node} 5 TO v 16 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики '--l- TO in_ 'r--- TO out_ include hart2.vf '---u TO in_ '---u TO out_ include fourier2.vf node} 6 TO v 17 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики 'r--- TO in_ '--l- TO out_ include hart2.vf '---u TO in_ '---u TO out_ include fourier2.vf node} 7 TO v 18 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики '--l- TO in_ '---u TO out_ include hart2.vf '---u TO in_ '---u TO out_ include fourier2.vf node} 8 TO v 28 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики '---u TO in_ '--l- TO out_ include hart2.vf $3fff0 # ~u/mod \ масштабирование коэффициентов '---u TO out_ out_ # b! dup !b \ передача результата ядрам, вычисляющим отсчеты Фурье спектра node} 9 TO v 27 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики '--l- TO in_ 'r--- TO out_ include hart2.vf $3fff0 # ~u/mod \ масштабирование коэффициентов '---u TO out_ out_ # b! dup !b node} 10 TO v 26 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p 'r--- TO in_ '--l- TO out_ include hart2.vf $3fff0 # ~u/mod \ масштабирование коэффициентов '---u TO out_ out_ # b! dup !b node} 11 TO v 25 {node 0 org coef , , , , , , , , , , , , , , , , here *cy \ включение режима расширенной арифметики '--l- TO in_ 'r--- TO out_ include hart2.vf $3fff0 # ~u/mod \ масштабирование коэффициентов '---u TO out_ out_ # b! dup !b node} 12 TO v 24 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики 'r--- TO in_ '--l- TO out_ include hart2.vf $3fff0 # ~u/mod \ масштабирование коэффициентов '---u TO out_ out_ # b! dup !b node} 13 TO v 23 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики '--l- TO in_ 'r--- TO out_ include hart2.vf $3fff0 # ~u/mod \ масштабирование коэффициентов '---u TO out_ out_ # b! dup !b node} 14 TO v 22 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики 'r--- TO in_ '--l- TO out_ include hart2.vf $3fff0 # ~u/mod \ масштабирование коэффициентов '---u TO out_ out_ # b! dup !b node} 15 TO v 21 {node 0 org coef , , , , , , , , , , , , , , , , here *cy =p \ включение режима расширенной арифметики '--l- TO in_ 'r--- TO out_ include hart2.vf $3fff0 # ~u/mod \ масштабирование коэффициентов '---u TO out_ out_ # b! dup !b node} reset \ сброс процессора 11 watch1 10 setstep \ настройка детального просмотра состояния 11-го ядра, задали шаг симулята в 10 "тактов" sim \ запуск симуляции
Вычисление коэффициентов Хартли
Рассмотрим вычисление отсчетов Хартли
Ядро вычисляет сумму произведений отсчетов сигнала на функцию cas(vt):
H(v)=Summ((t=0;N-1) f(t)*cas(2*pi*t*v/N)) ;
При необходимости масштабирование коэффициента выполняется отдельно.
Выполняются следующие действия:
- Начальная инициализация, промежуточная сумма s=0 ;
- Цикл от 0 до 15 из следующих ниже шагов;
- прием входного отсчета сигнала - f(t) ;
- копирование сигнала себе;
- передача отсчета сигнала соседнему ядру;
- выборка из таблицы значения cas(vt) ;
- вычисление произведения p=p*cas(vt) ;
- вычисление промежуточной суммы s=s+p ;
- если отсчет не 16й, возврат на начало цикла - п.3.
- H(v)=s.
Накопление суммы ведется с 36-ти битной точностью, входной сигнал и коэффициенты cas – 18-разрядные.
Файл hart2.vf
Код:
0 # 0 # 0 # \ на стеке начальная сумма в формате двойной точности и номер отсчета входного сигнала -- sh sl t 15 # for \ начинаем цикл вычисления H(v) \ -- sh sl t dup dup xor dup . + drop \ очищаем бит переноса in_ # b! @b \ -- sh sl t f(t) принимаем входной отсчет dup out_ # b! !b \ -- sh sl t f(t) скопировали на стек и передали следующему ядру \ выбираем из таблицы коэффициентов cas(vt) push \ сохраняем отсчет сигнала -- sh sl i r: -- f(t) a! @a+ \ -- sh sl cas; a=t+1; r: -- f(t) pop a@ push \ временно сохраняем номер отсчета на стеке возвратов -- sh sl cas f(t) ; r: -- t+1 ( умножаем f(t) на cas(vt) учитываем особенности реализации слова * - умноженная на 2 старшая часть на стеке; - сохранение множимого во втором элементе стека; в регистре а – младшая часть с инвертированным старшим битом ) * 2/ \ умножаем и приводим к нормальному виду старшую часть произведения \ -- sh sl cas cas*f(t) ; r: -- t+1 push drop pop \ избавляемся от множителя на второй позиции стека a@ $20000 # xor \ помещаем на стек младшую часть \ -- sh sl yh yl; r: -- t+1; где y=cas*f(t) \ производим сложение чисел двойной точности \ складываем младшие слова push a! \ -- sh sl; r: -- yl t+1; a=yh pop \ -- sh sl yl; r: -- t+1; a=yh . + \ -- sh sl+yl; r: -- t+1; a=yh \ складываем старшие слова push \ -- sh ; r: -- sl+yl t+1; a=yh a@ | . + pop \ -- sh+yh+c sl+yl t+1; r: -- t+1; a=yh pop \ возвращаем на стек сохраненный номер отсчета -- sh+yh+c sl+yl t+1; a=yh next \ -- hvh hvl t+1 drop \ сбрасываем ненужный теперь номер отсчета t -- hh hl - отсчет Хартли спектра
Вычисление преобразования Фурье
Вычисление коэффициентов Фурье спектра ведется на основе вычисленных ранее коэффициентов Хартли спектра. Последовательность действий следующая:
- получаем H(v) ;
- принимаем от соответствующего ядра отсчет H(N-v) ;
- вычисляем коэффициент Фурье спектра мощности Фр(v)=(H(v)^2+H(N-v)^2)/2 ;
Файл fourier2.vf
Код:
\ на стеке вычисленный в hart2.vf отсчет Хартли (старшая и младшая часть) -- hh hl $3fff0 # ~u/mod \ масштабируем коэффициент – делим на N (N=16) -- r q ( -- r hv ) ( получившийся остаток можно не учитывать, а поскольку переполнения стека в данном процессоре не бывает, можно считать ячейки стека ниже hv пустыми) in_ # b! @b \ принимаем масштабированный отсчет h(N-v) -- hv h(N-v) \ вычисляем H(N-v)^2 dup \ дублируем * \ умножаем \ приводим произведение к нормальному виду 2/ push drop pop \ -- hv f2h ; a=f2l a@ $20000 # xor \ -- hv f2h f2l ; a=f2l \ временно сохраняем его на стеке возвратов push push \ -- hv ; r: -- f2h f2l; \ вычисляем H(v)^2 dup \ дублируем * \ умножаем \ приводим произведение к нормальному виду 2/ push drop pop \ a@ $20000 # xor \ -- f1h f1l ; r: -- f2h f2l; \ складываем два числа с двойной точностью, одно из них находится на стеке возвратов pop a! pop \ взяли младшую часть слагаемого со стека возвратов -- f1h f1l f2l; a=f2h . + push \ сложили, результат запомнили на стеке возвратов -- f1h ; a=f2h ; r: -- f1l+f2l a@ . + pop \ сложили старшие части, младшую часть суммы вернули на стек -- Фрh Фрl \ на стеке удвоенный отсчет Фурье спектра мощности -- Фрh Фрl …….. \ далее может идти код, нацеленный на последующую обработку или передачу результатов '-d-- # b! @b \ останов ядра – исключительно для просмотра результата в симуляторе
Вспомогательный файл coef0.vf содержит слова, помогающие автоматизировать заполнение таблиц функции cas(vt).
Файл coef0.vf
Код:
: cas ( f: x -- ; -- cas ) \ вычисляет целочисленный вариант cas(x) fdup fcos fswap fsin f+ $01000 s>f f* f>s ; : 2pivt/N ( N v t -- ; f: -- 2pivt/N ) \ формирует аргумент для cas s>f s>f f* s>f f/ pi 2.e f* f* ; : coef ( -- cas[N] cas[N-1] … cas[0] ) \ оставляет на стеке N отсчетов в обратном порядке для заполнения таблицы cas(vt) 0 num ?do num v i 2pivt/N cas -1 +loop ;
Результаты
Оперативная память ядер вычисляющих и H(v) и Фр(v) загружена на 92% из которых 27% занято таблицей cas(vt).
Дамп памяти одного из ядер приведен ниже.
Код:
RAM Node 12 addr data mnemonics/code 000 01000 -- 001 014E8 ¦ 002 016A1 ¦ 003 014E8 ¦ 004 01000 ¦ 005 008A9 ¦ 006 00000 ¦ 007 3F757 ¦ 008 3F000 } таблица коэффициентов cas(vt) 009 3EB18 ¦ 00A 3E95F ¦ 00B 3EB18 ¦ 00C 3F000 ¦ 00D 3F757 ¦ 00E 00000 ¦ 00F 008A9 -- 010 05D17 @p+ @p+ @p+ @p+ --------------------------------- 011 00000 012 00000 013 00000 014 0000F 015 2E9B2 push . . . 016 24DE3 dup dup xor dup 017 2C1EF . + drop @p+ 018 00175 019 29F97 b! @b dup @p+ 01A 001D5 01B 29BBA b! !b push . 01C 2BC9A a! @a+ pop . вычисление H(v) 01D 228B2 a@ push . . 01E 134CA call CA * 01F 308EA 2/ push drop . 020 26E12 pop a@ @p+ . 021 20000 022 388AA xor push a! . 023 269F2 pop . + . 024 2EEB2 push a@ . . 025 2C19A . + pop . 026 27016 pop next 16 027 3BDB2 drop @p+ . . --------------------------------- 028 3FFF0 029 136AE call 2AE ~u/mod 02A 04B03 @p+ b! @b dup 02B 00145 02C 134CA call CA * 02D 308EA 2/ push drop . 02E 26E12 pop a@ @p+ . 02F 20000 030 388BB xor push push dup вычисление Фр(v) 031 134CA call CA * 032 308EA 2/ push drop . 033 26E12 pop a@ @p+ . 034 20000 035 38CAA xor pop a! . 036 269F2 pop . + . 037 2EEB0 push a@ . + 038 27DA2 pop @p+ b! . --------------------------------- 039 00115 03A 00000 @b and @b + 03B 03C 03D 03E 03F
Временные параметры вычислений следующие:
- вычисление коэффициента Хартли спектра ~ 2940 тактов;
- вычисление Фурье ~ 350 тактов;
- общее время выполнения ~ 3320 тактов.
С учетом того, что такт примерно соответствует 1.4нс, возможно выполнение примерно 215000 преобразований в секунду.
Оптимизация
Приведенный выше код для вычисления H(v) и Фр(v) не вполне соответствует форт стилю программирования и более напоминает код на ассемблере, что впрочем, для данного процессора не далеко от истины. Более оптимальным, с точки зрения структурного программирования и экономии места было бы определение слов M*, D+ с вызовом их в нужных местах.
Файл math.vf
Код:
: M* ( x y -- ph pl ) * 2/ \ -- x y ; push drop pop a@ $20000 # xor \ -- ph pl ; : D+ ( ah al bh bl -- sh sl ) push a! pop \ -- ah al bl; a=bh . + push \ -- ah ; a=bh ; r: -- sl a@ . + pop \ -- sh sl ;
Код для ядер изменится следующим образом. К примеру, код для 11-го ядра:
Код:
11 {node 0 org coef , , , , , , , , , , , , , , , , include math.vf here *cy =p 'r--- TO in_ '--l- TO out_ include hart2_.vf \ -- hh hl '---u TO in_ '---u TO out_ include fourier2_.vf \ -- fh fl node}
Аналогично, для остальных задействованных ядер после формирования таблицы, подключаются слова M* , D+.
Код для вычисления коэффициентов Хартли и Фурье примет вид
Файл hart2_.vf
Код:
0 # dup dup 15 # for dup dup xor dup . + drop \ очищаем бит переноса \ -- sh sl i in_ # b! @b \ -- sh sl i x приняли входной отсчет dup out_ # b! !b \ -- sh sl i x передали следующему push \ -- sh sl i r: -- x a! @a+ \ -- sh sl cas; a=i+1; r: -- x pop a@ push \ -- sh sl cas x ; r: -- i+1 M* \ -- sh sl yh yl; r: -- i+1; где y=cas*x D+ pop next \ -- hvh hvl i+1 - отсчет Хартли спектра drop \ -- hh hl \ '-d-- # b! @b \ останов ядра