Уровень 46: Окно в реальный мир
Задание
Реализуйте контроллер ввода-вывода с отображением в память (MMIO). Модуль IOController подключается к шине адреса и данных процессора:
- Запись по адресу 0xFF (addr=0xFF, we=1) — сохраняет data_in в регистр led_out и зажигает светодиоды.
- Чтение по адресу 0xFE (addr=0xFE, we=0) — выдаёт состояние переключателей switches_in на data_out.
Используйте generate-for для массива из 8 DFF-триггеров и AND-деревья для декодирования адресов. В интерфейсе появится панель «Периферия» с 8 светодиодами и 8 переключателями.
Материалы по теме
- Оперативная память — RAM и адресация
- Шины данных в Verilog — 8-битные подключения
Решение
Контроллер состоит из четырёх частей:
- Детектор адреса 0xFF: AND-дерево из 8 бит addr → addr_ff. Строб записи:
wr_en = addr_ff AND we. - Регистр led_out: generate-for (i=0..7) с DFF и логикой удержания: при wr_en=1 загружаем data_in[i], иначе сохраняем текущее значение led_out[i].
- Детектор адреса 0xFE: addr[7:1]=1, addr[0]=0 → addr_fe.
- Выход data_out: generate-for (j=0..7):
data_out[j] = switches_in[j] AND addr_fe(при addr_fe=1 выдаём переключатели, иначе 0).
module IOController(
input wire [7:0] addr,
input wire [7:0] data_in,
input wire we,
input wire clk,
input wire [7:0] switches_in,
output wire [7:0] data_out,
output wire [7:0] led_out
);
// Детектор 0xFF
wire t0, t1, t2, t3, t4, t5, addr_ff;
and a1(t0, addr[0], addr[1]);
and a2(t1, addr[2], addr[3]);
and a3(t2, addr[4], addr[5]);
and a4(t3, addr[6], addr[7]);
and a5(t4, t0, t1);
and a6(t5, t2, t3);
and a7(addr_ff, t4, t5);
wire wr_en, wr_not;
and aw(wr_en, addr_ff, we);
not nw(wr_not, wr_en);
// Детектор 0xFE
wire fe_a1, fe_a2, fe_a3, fe_a4, fe_a5, fe_hi, addr_0n, addr_fe;
and b1(fe_a1, addr[1], addr[2]);
and b2(fe_a2, addr[3], addr[4]);
and b3(fe_a3, addr[5], addr[6]);
and b4(fe_a4, fe_a1, fe_a2);
and b5(fe_a5, fe_a3, addr[7]);
and b6(fe_hi, fe_a4, fe_a5);
not b7(addr_0n, addr[0]);
and b8(addr_fe, fe_hi, addr_0n);
// Регистр led_out
generate
genvar i;
for (i = 0; i < 8; i = i + 1) begin : led_gen
wire t_on, t_hold, led_d;
and d1(t_on, data_in[i], wr_en);
and d2(t_hold, led_out[i], wr_not);
or d3(led_d, t_on, t_hold);
dff dff_i(led_out[i], led_d, clk);
end
endgenerate
// Выход data_out
generate
genvar j;
for (j = 0; j < 8; j = j + 1) begin : data_gen
and gm(data_out[j], switches_in[j], addr_fe);
end
endgenerate
endmodule
Ключевая идея: wr_not = NOT wr_en. Когда wr_en=1: led_d = data_in[i] AND 1 = data_in[i] (загрузка). Когда wr_en=0: led_d = led_out[i] AND 1 = led_out[i] (удержание). DFF захватывает led_d по фронту clk.