В мире асинхронной логики всё не так. Если для традиционной логики основные кубики это триггеры, защёлки, мультиплексоры и т.п., то здесь набор элементарных компонентов совсем другой. Теоретическое обоснование дал математик Кис ван Беркель в книжке "Handshake circuits: an asynchronous architecture for VLSI programming" (есть ранний вариант в виде PDF). Более практичный список асинхронных примитивов можно найти в главе 13 "The Breeze Components" документации по компилятору Balsa.
Принципы построения асинхронных компонентов:
1. Каждый компонент имеет один или несколько портов.
2. Каждый порт состоит из двух направлений: передача и приём.
3. Направление передачи может состоять из нескольких проводов, при этом приём имеет всего один провод. Такой порт называется передающим (output).
4. Направление приёма может состоять из нескольких проводов, при этом передача имеет всего один провод. Такой порт называется принимающим (input).
5. Направление приёма и направление передачи могут иметь по одному проводу. Такой порт называется синхронизирующим (sync или nonput).
6. Порт называется ведущим (master), если сначала становится активным (ненулевым) передающее направление, и затем ожидается ответ на приёмном направлении.
7. Порт называется ведомым (slave), если сначала ожидается ненулевой сигнал на приёмном направлении, и затем формируется ответ на передающем направлении.
8. Компонент обязан подчиняться принципу самоинициализации: когда все входные сигналы всех портов равны нулю (неактивны), все выходные сигналы также обязаны перейти в нулевое состояние.
Таким образом, возможны шесть типов портов: master_output, master_input, slave_output, slave_input, sync_output и sync_input. Используя конструцию interface языка SystemVerilog из предыдущего поста эти шесть типов можно определить следующим образом:
Попробуем определить, сколько и каких однопортовых примитивов можно наваять при таком подходе. Из принципа самоинициализации следует, что единственный порт не может быть ведущим. Остаётся три варианта: ведомый передающий, ведомый принимающий и ведомый синхронизирующий.
Constant
Примитив с единственным ведомым передающим портом служит для выдачи некоторого постоянного значения.
Компонент с одним ведомым синхронизирующим портом отправляет подтверждение сразу, как только получает запрос. Очень похоже на оператор "continue" в языках программирования. У ван Беркеля он называется "Run".
Компонент с одним ведомым принимающим портом работает так же, как Continue, но получает на вход некоторое численное значение. Он может применяться, к примеру, для отображения числа на некотором дисплее и т.п.
Кроме Continue, можно представить себе ещё один вариант компонента с одним ведомым синхронизирующим портом: который вообще никогда не отправляет подтверждение. Фактически он приводит к остановке работы логической схемы, поэтому называется "Halt". Ван Беркель называет его "Stop".
Этот компонент с одним ведомым принимающим портом принимает численное значение и останавливает работу схемы.
Принципы построения асинхронных компонентов:
1. Каждый компонент имеет один или несколько портов.
2. Каждый порт состоит из двух направлений: передача и приём.
3. Направление передачи может состоять из нескольких проводов, при этом приём имеет всего один провод. Такой порт называется передающим (output).
4. Направление приёма может состоять из нескольких проводов, при этом передача имеет всего один провод. Такой порт называется принимающим (input).
5. Направление приёма и направление передачи могут иметь по одному проводу. Такой порт называется синхронизирующим (sync или nonput).
6. Порт называется ведущим (master), если сначала становится активным (ненулевым) передающее направление, и затем ожидается ответ на приёмном направлении.
7. Порт называется ведомым (slave), если сначала ожидается ненулевой сигнал на приёмном направлении, и затем формируется ответ на передающем направлении.
8. Компонент обязан подчиняться принципу самоинициализации: когда все входные сигналы всех портов равны нулю (неактивны), все выходные сигналы также обязаны перейти в нулевое состояние.
Таким образом, возможны шесть типов портов: master_output, master_input, slave_output, slave_input, sync_output и sync_input. Используя конструцию interface языка SystemVerilog из предыдущего поста эти шесть типов можно определить следующим образом:
dual_rail#(4) x(); // Канал шириной 4 бита
dual_rail#(8) y(); // Канал шириной 8 бит
sync z(); // Синхронизирующий канал
Component1 c1 (x.master_output, y.slave_output, z.slave);
Component2 c2 (y.master_input, x.slave_input);
Component3 c3 (z.master);
Это соответствует следующей картинке:
_______ _______ _______
| | | | x | |
| | z | |------->o| |
| c3 |--------o| c1 | | c2 |
| | | | y | |
|_______| |_______|o------->|_______|Здесь компонент с1 имеет три порта: один ведущий (x) и два ведомых (y и z). Ведомые порты принято обозначать кружочком. Направление передачи канала обозначается стрелочкой. Синхронизирующий канал не имеет стрелочки.Попробуем определить, сколько и каких однопортовых примитивов можно наваять при таком подходе. Из принципа самоинициализации следует, что единственный порт не может быть ведущим. Остаётся три варианта: ведомый передающий, ведомый принимающий и ведомый синхронизирующий.
Constant
Примитив с единственным ведомым передающим портом служит для выдачи некоторого постоянного значения.
// ,---.
// | V |o-->
// `---'
//
module Constant #(parameter VALUE = '0) (dual_rail.slave_output x);
assign x.hidata = x.req ? VALUE : '0;
assign x.lodata = x.req ? ~VALUE : '0;;
endmoduleContinueКомпонент с одним ведомым синхронизирующим портом отправляет подтверждение сразу, как только получает запрос. Очень похоже на оператор "continue" в языках программирования. У ван Беркеля он называется "Run".
// ,---.
// ---o|run|
// `---'
//
module Continue (sync.slave x);
assign x.ack = x.req;
endmoduleContinuePushКомпонент с одним ведомым принимающим портом работает так же, как Continue, но получает на вход некоторое численное значение. Он может применяться, к примеру, для отображения числа на некотором дисплее и т.п.
// ,---.
// -->o|run|
// `---'
//
module ContinuePush (dual_rail.slave_input x);
wire set = &(x.hidata | x.lodata);
wire reset = (x.hidata == '0) & (x.lodata == '0);
always @(*) // Защёлка
if (set)
x.req = '1;
else if (reset)
x.req = '0;
endmoduleHaltКроме Continue, можно представить себе ещё один вариант компонента с одним ведомым синхронизирующим портом: который вообще никогда не отправляет подтверждение. Фактически он приводит к остановке работы логической схемы, поэтому называется "Halt". Ван Беркель называет его "Stop".
// ,---.
// ---o|stp|
// `---'
//
module Halt (sync.slave x);
assign x.ack = '0;
endmodule
HaltPushЭтот компонент с одним ведомым принимающим портом принимает численное значение и останавливает работу схемы.
// ,---.
// -->o|stp|
// `---'
//
module HaltPush (dual_rail.slave_input x);
assign x.req = '0;
endmoduleКак можно видеть, уже с одним портом мы имеем интересный набор компонентов. Всего же в наборе языка Balsa имеется 28 примитивов с двумя, 12 с тремя и 4 с четырьмя портами. Со временем про них тоже напишу.
no subject
Date: 2017-02-03 07:13 (UTC)no subject
Date: 2017-02-03 07:52 (UTC)no subject
Date: 2017-02-03 16:23 (UTC)no subject
Date: 2017-02-03 20:37 (UTC)no subject
Date: 2017-02-03 21:25 (UTC)Взлетит, но потом. Когда инженеры на пенсию уйдут.
Thanks, -Vlad