56 / 63 · Теория
Регистровый файл с двумя чтениями, одной записью и bypass
Реализуйте два независимых порта чтения и синхронную запись, проверьте bypass для каждой коллизии и правила инициализации и valid.
Уроки доступны бесплатно. Запишитесь, чтобы сохранять прогресс.
Хранилище, выдающее два значения одновременно
Для A+B нужны два операнда в одном цикле. РегистровыйРегистр хранит многобитное состояние. Синхронные регистры в этом курсе записывают определённый вход на фронте тактового сигнала. Подробнее файл 2-read/1-write предоставляет два адреса чтения и один записи. Здесь хранится восемь 16-битных слов, оба чтения зарегистрированы по положительному фронту. Адрес имеет 3 бита, ёмкость 8×16=128 бит. Адрес 0 — обычное хранилище без правила константного регистра.
У каждого чтения свои enable и valid. Один порт может простаивать, пока другой читает; разрешено чтение одного адреса обоими. Возможность одновременно читать разные адреса имеет стоимость, отличную от простого увеличения ёмкости. Малый файл можно реализовать массивом триггеровТриггер записывает вход на заданном фронте такта и удерживает его до следующего фронта. Вход должен удовлетворять ограничениям setup и hold. Подробнее и двумя сетями выбора. Для определённой RAM возможна репликация памяти с общей записью ради нужного числа портов. Ресурс синтеза нельзя определить только по объявлению RTL.
Когда записываемое значение одновременно читается
Контракт возвращает новые данные при чтении и записи одного адреса на одном фронте. Bypass направляет wr_data прямо на вход выходного регистра чтения, не ожидая обновления массива. Для порта условие коллизии:
При ответ:
— хранимое значение до фронта. отдельно сравнивает каждый адрес чтения. Если из-за коллизии одного порта направить запись на оба, исказится и операнд другого адреса. Пропуск wr_en также создаст ложный обход при случайном совпадении wr_addr без реальной записи.
module regfile8x16_2r1w (
input logic clk, rst, wr_en,
input logic [2:0] wr_addr,
input logic [15:0] wr_data,
input logic rd_en0, rd_en1,
input logic [2:0] rd_addr0, rd_addr1,
output logic [15:0] rd_data0, rd_data1,
output logic rd_valid0, rd_valid1
);
timeunit 1ns;
timeprecision 1ps;
logic [15:0] mem [0:7];
always_ff @(posedge clk) begin
if (rst) begin
rd_valid0 <= 1'b0;
rd_valid1 <= 1'b0;
end else begin
rd_valid0 <= rd_en0;
rd_valid1 <= rd_en1;
if (wr_en)
mem[wr_addr] <= wr_data;
if (rd_en0)
rd_data0 <= (wr_en && wr_addr == rd_addr0)
? wr_data : mem[rd_addr0];
if (rd_en1)
rd_data1 <= (wr_en && wr_addr == rd_addr1)
? wr_data : mem[rd_addr1];
end
end
endmoduleBypass меняет выбор входа, но не удаляет выходную регистровую ступень. Адрес и запрос подаются до фронта, ответ наблюдается после него. Следующий регистр получает ответ на следующем положительном фронте. Переход к комбинационному чтению меняет и путь от адреса к выходу, и контракт задержки; это не просто сокращение кода.
Пример: коллизия лишь одного порта
Пусть заранее записаны M[2]=12, M[5]=7. Перед E0 включите оба чтения: rd_addr0=2, rd_addr1=5, wr_en=1, wr_addr=2, wr_data=20. После E0 порт 0 отвечает обходным 20, порт 1 — сохранённым 7. Сумма действительных ответов 27, в отличие от 19 у read-first без bypass.
На E1 задайте оба адреса чтения 2 и запишите туда 30: оба выхода 30. На E2 при wr_en=0, wr_addr=2, wr_data=99 читайте адрес 2: оба выхода должны быть 30. Этот цикл выявляет обход только по совпадению адреса. На E3 при rd_en0=0, rd_en1=1 valid порта 0 равен 0, порта 1 — 1.
Каждый столбец — запись наблюдения одного положительного фронтаПоложительный фронт — момент перехода такта из 0 в 1. Его следует отличать от уровня, означающего весь интервал CLK=1. Подробнее. Входы показаны непосредственно перед фронтом, состояния со словом «после» в имени — сразу после обновления. Выравнивание столбцов обозначает порядок выборок, а не физическую задержку распространенияЗадержка распространения — время от изменения входа до установления правильного выхода. Логическая эквивалентность выражений и их временные характеристики — разные вопросы. Подробнее.
Предварительно M[2]=12, M[5]=7, оба enable чтения всегда 1. Адрес записи — 2. В последнем столбце wr_en=0, поэтому обход wr_data=99 запрещён.
Показать данные диаграммы
| Сигнал | Диаграмма | Значения шины |
|---|---|---|
| фронт | 234 | E0 → E1 → E2 |
| wr_en | 1.0 | |
| wr_data | 234 | 20 → 30 → 99 |
| rd_addr0 | 2.. | 2 |
| rd_addr1 | 23. | 5 → 2 |
| out0 после | 23. | 20 → 30 |
| out1 после | 23. | 7 → 30 |
Что нужно инициализировать
Синхронный active-high reset блокирует запись и обнуляет оба valid. Массив и выходные данные не сбрасываются. Поэтому сохранённая на выходной шине старая величина при valid=0 после reset нормальна. Этот RTL не стирает прежнее содержимое массива. Если система после reset допускает лишь новую работу, определите её инициализацию отдельно.
Первое чтение адреса одновременно с его первой записью здесь получает определённое новое значение благодаря bypass. Но чтение никогда не инициализированного адреса без записи остаётся неопределённым. Rd_valid обозначает момент ответа, а не initialized-бит адреса. Эталон перед обычным чтением проверяет известность адреса и отдельно обрабатывает обход того же фронта.
Проверяйте четыре сочетания enable чтения, одинаковые и разные адреса, коллизию ни с одним портом, с 0, с 1, с обоими, последовательные записи и приоритет reset. Несколько портов записи вне рамок лекции; при добавлении сначала задайте приоритет двух записей в один адрес. Изменение числа портов меняет и функциональный контракт, и стоимость ресурсов.
Попробуйте сами
M[1]=4, M[4]=9. Оба чтения разрешены по адресам 1 и 4; в 4 записывают 13. Какова пара выходов? На следующем фронте wr_en=0, wr_addr=1, wr_data=99, оба адреса чтения 1. Каков результат? Наконец, выключите только enable порта 0 и объясните valid и потребление данных.
Прочитать объяснение
Первый ответ (4,13): только порт 1 совпадает с адресом записи и получает новое 13 через bypass. Следующий (4,4): при wr_en=0 значение 99 не используется. В последнем цикле rd_valid0=0, rd_valid1=1. Регистр данных порта 0 хранит прежнее значение, которое нельзя потреблять как действительный ответ. Адрес 0 тоже читается и записывается как обычный.