Уровень 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 переключателями.

Материалы по теме

Решение

Контроллер состоит из четырёх частей:

  1. Детектор адреса 0xFF: AND-дерево из 8 бит addr → addr_ff. Строб записи: wr_en = addr_ff AND we.
  2. Регистр led_out: generate-for (i=0..7) с DFF и логикой удержания: при wr_en=1 загружаем data_in[i], иначе сохраняем текущее значение led_out[i].
  3. Детектор адреса 0xFE: addr[7:1]=1, addr[0]=0 → addr_fe.
  4. Выход 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.