Господа, много ли среди вас программистов? Поднимем руки ... Да, много.
А тех, кто знает, как дизайнится хардвер?
"С помощью паяльника"? Хм, не только.
"Рисуют дорожки микросхемы и транзисторы мышкой в Фотошопе"? Так делали в 1970-х годах и та программа называлась не "Фотошоп".
"Весь хардвер уже задизайнен в Интеле, а всё, что поверх Интела - это микрокод". Нет, "микрокод" не превращается в дорожки и транзисторы, а Verilog превращается. И интеловский процессор - это только частный случай хардвера.
Disclaimer: Далее идет мой текст про вещи, которые, по утверждениям ряда комментаторов, сейчас учат в средней школе. Даже если это так (я сам выучил эту хрень в 24 года в 1995 году в Mentor Graphics Corp), но даже если это сейчас учат в школе, то я все равно это приведу, ибо пришлось уже несколько раз объяснять, что Verilog - это не микрокод, а также зачем нужен clock. Кроме этого, среди читателей моего блога могут оказаться младшие школьники, для которых это тоже полезно (гейты и флип-флопы при СССР были в книжках для третьеклассников и в польском журнале "Горизонты техники для детей").
Ну ладно, не буду вас томить. В наше время чаще всего дизайн пишется на языке Verilog (в Европе и у военных - VHDL), после чего специальная программа (logic synthesis) превращает дизайн в граф из проводов и логических примитивов, другая программа (static timing analysis) сообщает дизайнеру, вписывается ли он в бюджет скорости, а третья программа (place-and-route) раскладывает этот дизайн по площадке микросхемы.
Допустим, нам нужно сделать микросхему - счетчик, которая бы выводила в цикле числа 0, 1, 2, 3, 0, 1, 2, 3, ...
Пишем на SystemVerilog:
module counter
(
input clock,
input reset,
output logic [1:0] n
);
always @(posedge clock)
begin
if (reset)
n <= 0;
else
n <= n + 1;
end
endmodule
Это означает: Имеется модуль, в который идет два провода - clock (синхросигнал, который идет вверх-вниз-вверх-вниз-...) и reset (сброс в начальное состояние). Из модуля выходит тоже два провода, которые образуют одно двухбитное число, которое на каждом биении синхросигнала увеличивают значение на единицу (по модулю 4). Иными словами: на каждом положительном фронте сигнала (clock edge) смотреть на провод reset. Если reset равен 1, то обнулить внутренний регистр (подсоединенный к выводу), если reset равен 0, то увеличить число, хранящееся в этом регистре на единицу.
Теперь если скормить этот код синтезис-тулу, в нашем случае Synopsys Design Compiler, то он превратит его вот в такую схему:
Если покрупнее фрагмент:
Схема выглядит мудрено, но на самом деле она работает так:
Разъяснение что есть что:
Квадратик слева - сумматор, такой комбинаторный дизайн, которые берет на входе два числа и через некоторое время T выдает на выходе их сумму. До истечения времени T на выходе сумматора будут находиться какие-то неопределенные значения, а после T все устаканится.
Время T должно быть меньше, чем время между пульсами синхросигнала. Точнее, наоборот. Дизайнер задаёт программе-синтесайзеру, с какой частотой он хочет гонять дизайн, и программа подбирает подходящий сумматор (быстрый-большой, медленный-маленький и т.д.) под эту частоту. Если получится.
Квадратик справа - это флип-флоп (называемая по-русски "триггер"), такая фиговина, которая хранит бит информации между пульсами синхросигнала. Пока всякая арифметика в сумматоре устаканивается, булькает и чавкает, в это время флип-флоп хранит значение с предыдущего цикла часов/синхросигнала и отдает его только в качестве read-only значения для вычислений.
В конце-концов вся комбинаторная логика (сумматор) устаканивается, и наконец приходит положительный фронт синхросигнала, который разрешает флип-флопу записать новые значения для следующего цикла.
С помощью модулей, гейтов (элементов комбинаторной логики - "и", "или", "нет" и т.д.) и флип-флопов в принципе можно реализовать весь хардвер. И любой алгоритм. Причем с высокой степенью параллельности. Можно хоть сто чисел складывать за наносекунду, если очень хочется и есть достаточно много гейтов и ваттов (хотя про ватты - это более тонкий вопрос).
А теперь товарищи программисты - вопрос с места "Ну и как эту хреновину отлаживать?"
А очень просто. В язык Verilog входит не только "synthesizable subset" (подмножество языка, код на котором в конечном итоге становится силиконовыми дорожками и силиконовыми транзисторами, переключающимися за наносекунды).
В верилог входит еще и "behavioral level", кучу конструкций для обыкновенного программирования, которые позволяют писать на верилоге высокоуровневые модели и тесты для симуляции блоков, процессоров и систем на обычных компьютерах.
Например, тест для нашего счетчика может выглядеть так:
module testbench;
// Декларируем провода
logic clock;
logic reset;
logic [1:0] n;
// Адын инстанс модуля counter
counter counter_instance (clock, reset, n);
// Инициализируем и врубаем синхросигнал,
// меняющий значение каждые 5 условных наносекунд
initial clock = 0;
always # 5 clock = ~ clock;
// Через два цикла синхросигнала запускаем reset,
// Держим его еще два цикла, запускаем обратно
initial
begin
reset <= 0;
repeat (2) @(posedge clock);
reset <= 1;
repeat (2) @(posedge clock);
reset <= 0;
end
initial
begin
// Записываем все изменения сигналов в файл
$dumpvars (0, counter_instance);
// Печатаем на экране
$display ("time clock reset n");
$monitor (" %2d %b %b %b", $time, clock, reset, n);
repeat (10) @(posedge clock);
$finish;
end
endmodule
Теперь запустим все это дело на симуляторе верилога, например Cadence NC-Verilog:
ncverilog +access+r +sv counter.sv testbench.sv
ncsim> run
time clock reset n
0 0 0 xx
5 1 0 xx
10 0 0 xx
15 1 1 xx
20 0 1 xx
25 1 1 00
30 0 1 00
35 1 0 00
40 0 0 00
45 1 0 01
50 0 0 01
55 1 0 10
60 0 0 10
65 1 0 11
70 0 0 11
75 1 0 00
80 0 0 00
85 1 0 01
90 0 0 01
Simulation complete via $finish(1) at time 95 NS + 0
Но харвер-инжинеры и хардвер-верификейшн-инжинеры редко отлаживаются с помощью printf-ов, то бишь $display-statement-ов. Гораздо удобнее смотреть на сигналы с помощью так называемого waveform viewer:
Все! Вы, программисты, теперь стали хардверщиками. Вы рады?
Poll Господа! Испытываете ли вы радость от моего поста?