mirror of
https://github.com/MPSU/APS.git
synced 2026-06-13 12:33:33 +00:00
English version draft
Assisted-by: Claude:claude-4.6-sonnet
This commit is contained in:
@@ -1,73 +1,73 @@
|
||||
# Лабораторная работа №8 "Блок загрузки и сохранения"
|
||||
# Lab 8. Load/Store Unit
|
||||
|
||||
Итогом седьмой лабораторной работы стал практически завершенный процессор архитектуры RISC-V. Особенностью реализации процессора было отсутствие поддержки инструкций `LB`, `LBU`, `SB`, `LH`, `LHU`, `SH`. Тому было две причины:
|
||||
The result of the seventh lab was a nearly complete RISC-V processor. A notable limitation of that implementation was the lack of support for the `LB`, `LBU`, `SB`, `LH`, `LHU`, and `SH` instructions. There were two reasons for this:
|
||||
|
||||
- подключенный к памяти данных сигнал `byte_enable_i` был аппаратно зафиксирован на значении `4'b1111`, но на самом деле этим сигналом должен кто-то управлять;
|
||||
- необходимо подготовить считанные из памяти полуслова / байты для записи в регистровый файл.
|
||||
- the `byte_enable_i` signal connected to the data memory was hardwired to `4'b1111`, but in reality this signal needs to be actively driven;
|
||||
- halfwords and bytes read from memory must be prepared before being written to the register file.
|
||||
|
||||
Для этих целей используется специальный модуль — **Блок загрузки и сохранения** (**Load and Store Unit**, **LSU**).
|
||||
A dedicated module is used for these purposes — the **Load/Store Unit** (**LSU**).
|
||||
|
||||
## Цель
|
||||
## Objective
|
||||
|
||||
Разработка блока загрузки и сохранения для подключения к внешней памяти данных, поддерживающей запись в отдельные байты памяти.
|
||||
Design a load/store unit for interfacing with external data memory that supports byte-granular write access.
|
||||
|
||||
---
|
||||
|
||||
## Ход работы
|
||||
## Workflow
|
||||
|
||||
Изучить:
|
||||
Study:
|
||||
|
||||
- Функции и задачи блока загрузки/сохранения
|
||||
- Интерфейс процессора и блока загрузки/сохранения
|
||||
- Интерфейс блока загрузки/сохранения и памяти
|
||||
- The functions and responsibilities of the load/store unit
|
||||
- The interface between the processor core and the load/store unit
|
||||
- The interface between the load/store unit and memory
|
||||
|
||||
Реализовать и проверить модуль `lsu`.
|
||||
Implement and verify the `lsu` module.
|
||||
|
||||
---
|
||||
|
||||
## Теория
|
||||
## Theory
|
||||
|
||||
Модуль загрузки и сохранения (**Load/Store Unit** – **LSU**) служит для исполнения инструкций типа `LOAD` и `STORE`: является прослойкой между внешним устройством – памятью, и ядром процессора. **LSU** считывает содержимое из памяти данных или записывает в нее требуемые значения, преобразуя 8- и 16-битные данные в знаковые или беззнаковые 32-битные числа для регистров процессора. В процессорах с **RISC** архитектурой с помощью **LSU** осуществляется обмен данными между регистрами общего назначения и памятью данных.
|
||||
The **Load/Store Unit** (**LSU**) is responsible for executing `LOAD` and `STORE` instructions. It acts as an intermediary between the external memory and the processor core. The **LSU** reads data from data memory or writes required values to it, converting 8- and 16-bit data into signed or unsigned 32-bit values for the processor registers. In **RISC** architecture processors, the **LSU** handles all data exchanges between general-purpose registers and data memory.
|
||||
|
||||

|
||||
|
||||
_Рисунок 1. Место LSU в микроархитектуре RISC-процессора._
|
||||
_Figure 1. The place of the LSU in the RISC processor microarchitecture._
|
||||
|
||||
### Интерфейс процессора и блока загрузки/сохранения
|
||||
### Processor Core and LSU Interface
|
||||
|
||||
На входной порт `core_addr_i` от процессора поступает адрес ячейки памяти, к которой будет произведено обращение. Намеренье процессора обратиться к памяти (как для чтения, так и для записи) отражается выставлением сигнала `core_req_i` в единицу. Если процессор собирается записывать в память, то сигнал `core_we_i` выставляется в единицу, а сами данные, которые следует записать, поступают от него на вход `core_wd_i`. Если процессор собирается читать из памяти, то сигнал `core_we_i` находится в нуле, а считанные данные подаются на выход `core_rd_o`.
|
||||
The processor sends the address of the memory location to be accessed via the `core_addr_i` input port. The processor's intent to access memory (for either read or write) is indicated by asserting the `core_req_i` signal. If the processor intends to write to memory, the `core_we_i` signal is asserted and the data to be written is provided on the `core_wd_i` input. If the processor intends to read from memory, `core_we_i` is deasserted and the read data is presented on the `core_rd_o` output.
|
||||
|
||||
Инструкции `LOAD` и `STORE` в **RV32I** поддерживают обмен 8-битными, 16-битными или 32-битными значениями, однако в самом процессоре происходит работа только с 32-битными числами, поэтому загружаемые из памяти байты и послуслова необходимо предварительно расширить до 32-битного значения. Расширять значения можно либо знаковым битом, либо нулями — в зависимости от того, как должно быть интерпретировано загружаемое число: как знаковое или беззнаковое. Во время записи данных в память, они не расширяются, поскольку в отличие от регистрового файла, основная память имеет возможность обновлять отдельные байты. Таким образом, различать знаковые и беззнаковые числа необходимо только на этапе загрузки, но не сохранения.
|
||||
The `LOAD` and `STORE` instructions in **RV32I** support exchanges of 8-bit, 16-bit, or 32-bit values. However, the processor itself only operates on 32-bit numbers, so bytes and halfwords loaded from memory must first be extended to 32-bit values. Extension can be performed either by sign extension or by zero extension, depending on whether the loaded value is to be interpreted as signed or unsigned. During store operations, no extension is needed, since unlike the register file, main memory supports byte-level updates. Therefore, the distinction between signed and unsigned values is only relevant during load operations.
|
||||
|
||||
Для выбора разрядности и формата представления числа на вход **LSU** подаётся сигнал `core_size_i`, принимающий следующие значения (для удобства использования, данные значения определены в виде параметров в пакете `decoder_pkg`):
|
||||
The `core_size_i` signal is provided to the **LSU** to select the data width and representation format. It takes the following values (defined as parameters in the `decoder_pkg` package for convenience):
|
||||
|
||||
| Параметр |Значение| Пояснение |
|
||||
|----------|--------|-------------------------------|
|
||||
| LDST_B | 3'd0 |Знаковое 8-битное значение |
|
||||
| LDST_H | 3'd1 |Знаковое 16-битное значение |
|
||||
| LDST_W | 3'd2 |32-битное значение |
|
||||
| LDST_BU | 3'd4 |Беззнаковое 8-битное значение |
|
||||
| LDST_HU | 3'd5 |Беззнаковое 16-битное значение |
|
||||
| Parameter | Value | Description |
|
||||
|-----------|--------|-----------------------------------|
|
||||
| LDST_B | 3'd0 | Signed 8-bit value |
|
||||
| LDST_H | 3'd1 | Signed 16-bit value |
|
||||
| LDST_W | 3'd2 | 32-bit value |
|
||||
| LDST_BU | 3'd4 | Unsigned 8-bit value |
|
||||
| LDST_HU | 3'd5 | Unsigned 16-bit value |
|
||||
|
||||
Для операций типа `STORE` формат представления чисел не важен, для них `core_size_i` сможет принимать значение только от 0 до 2.
|
||||
For `STORE` operations, the data representation format is irrelevant; for these instructions, `core_size_i` can only take values from 0 to 2.
|
||||
|
||||
Выходной сигнал `core_stall_o` нужен для приостановки программного счётчика. Ранее логика этого сигнала временно находилась в модуле `processor_system` — теперь она займёт своё законное место в модуле **LSU**.
|
||||
The `core_stall_o` output signal is used to stall the program counter while a memory access is in progress. Previously, the logic for this signal was temporarily located in the `processor_system` module — it now takes its proper place inside the **LSU** module.
|
||||
|
||||
### Интерфейс блока загрузки/сохранения и памяти
|
||||
### LSU and Memory Interface
|
||||
|
||||
Память данных имеет 32-битную разрядность ячейки памяти и поддерживает побайтовую адресацию. Это значит, что существует возможность обновить любой байт в пределах одного слова (4-байтовой ячейки памяти), не изменяя слова целиком. Для указания на обновляемые байты интерфейс к памяти предусматривает использование 4-битного сигнала `mem_be_o`, подаваемого вместе с адресом слова `mem_addr_o`. Позиции битов 4-битного сигнала соответствуют позициям байт в слове. Если конкретный бит `mem_be_o` равен 1, то соответствующий ему байт в памяти будет обновлен. Данные для записи подаются на выход `mem_wd_o`. На результат чтения из памяти состояние `mem_be_o` не влияет, так как чтение производится всегда по 32-бита.
|
||||
The data memory has 32-bit wide cells and supports byte-addressable access. This means it is possible to update any individual byte within a word (a 4-byte memory cell) without modifying the entire word. To indicate which bytes are to be updated, the memory interface uses the 4-bit `mem_be_o` (byte enable) signal, which is provided alongside the word address `mem_addr_o`. Each bit position in the 4-bit signal corresponds to the position of a byte within the word. If a specific bit of `mem_be_o` is 1, the corresponding byte in memory will be updated. Write data is provided on the `mem_wd_o` output. The state of `mem_be_o` does not affect read operations, as reads always return the full 32-bit word.
|
||||
|
||||
После получения запроса на чтение/запись из ядра, **LSU** перенаправляет запрос в память данных, взаимодействие осуществляется следующими сигналами:
|
||||
Upon receiving a read or write request from the core, the **LSU** forwards the request to data memory using the following signals:
|
||||
|
||||
- сигнал `mem_req_o` сообщает памяти о наличии запроса в память (напрямую подключён к `core_req_i`);
|
||||
- сигнал `mem_we_o` сообщает памяти о типе этого запроса (напрямую подключён к `core_we_i`):
|
||||
- `mem_we_o` равен 1, если отправлен запрос на запись,
|
||||
- `mem_we_o` равен 0, если отправлен запрос на чтение;
|
||||
- сигнал `mem_wd_o` содержит данные на запись в память. В зависимости от размера записи, данные этого сигнала будут отличаться от пришедшего сигнала `core_wd_i` и будут является результатом определённых преобразований.
|
||||
- сигнал `mem_rd_i` содержит считанные из памяти данные. Перед тем, как вернуть считанные данные ядру через выходной сигнал `core_rd_o`, эти данные будет необходимо подготовить.
|
||||
- сигнал `mem_ready_i` сообщает о готовности памяти завершить транзакцию на текущем такте. Этот сигнал используется для управления выходным сигналом `core_stall_o`.
|
||||
- `mem_req_o` — notifies memory of an incoming request (directly connected to `core_req_i`);
|
||||
- `mem_we_o` — indicates the type of request (directly connected to `core_we_i`):
|
||||
- `mem_we_o` equals 1 for a write request,
|
||||
- `mem_we_o` equals 0 for a read request;
|
||||
- `mem_wd_o` — contains the data to be written to memory. Depending on the transfer size, the data on this signal may differ from the incoming `core_wd_i` signal and will be the result of certain transformations;
|
||||
- `mem_rd_i` — contains the data read from memory. Before returning read data to the core via the `core_rd_o` output, this data must be prepared;
|
||||
- `mem_ready_i` — indicates that memory is ready to complete the transaction on the current clock cycle. This signal is used to control the `core_stall_o` output.
|
||||
|
||||
<!-- |Команды| Byte Offset | lsu_data_o |
|
||||
<!-- |Command| Byte Offset | lsu_data_o |
|
||||
|-------|-------------|-------------------------------------------------|
|
||||
| lb | 00 | {{24{data_rd_i[7]}}, data_rd_i[7:0]} |
|
||||
| | 01 | {{24{data_rd_i[15]}}, data_rd_i[15:8]} |
|
||||
@@ -76,14 +76,14 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр
|
||||
| lh | 00 | {{16{data_rd_i[15]}}, da-ta_rd_i[15:0]} |
|
||||
| | 10 | {{16{data_rd_i[31]}}, da-ta_rd_i[31:16]} |
|
||||
| lw | 00 | data_rd_i[31:0] |
|
||||
| lbu | 00 | {24’b0, data_rd_i[7:0]} |
|
||||
| | 01 | {24’b0, data_rd_i[15:8]} |
|
||||
| | 10 | {24’b0, data_rd_i[23:16]} |
|
||||
| | 11 | {24’b0, data_rd_i[31:24]} |
|
||||
| lhu | 00 | {16’b0, data_rd_i[15:0]} |
|
||||
| | 10 | {16’b0, data_rd_i[31:16]} | -->
|
||||
| lbu | 00 | {24'b0, data_rd_i[7:0]} |
|
||||
| | 01 | {24'b0, data_rd_i[15:8]} |
|
||||
| | 10 | {24'b0, data_rd_i[23:16]} |
|
||||
| | 11 | {24'b0, data_rd_i[31:24]} |
|
||||
| lhu | 00 | {16'b0, data_rd_i[15:0]} |
|
||||
| | 10 | {16'b0, data_rd_i[31:16]} | -->
|
||||
|
||||
<!-- |Команды| Byte Offset | data_wd_o | data_be_o |
|
||||
<!-- |Command| Byte Offset | data_wd_o | data_be_o |
|
||||
| sb | 00 | { 4{lsu_data_i[7:0]} } | 0001 |
|
||||
| | 01 | | 0010 |
|
||||
| | 10 | | 0100 |
|
||||
@@ -94,44 +94,44 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр
|
||||
|
||||
---
|
||||
|
||||
## Практика
|
||||
## Practice
|
||||
|
||||
> Познай как описать выходные сигналы модуля — и ты познаешь как описать сам модуль. ©Джейсон Стейтем
|
||||
> Know how to describe the output signals of a module — and you will know how to describe the module itself. ©Jason Statham
|
||||
|
||||
Реализация любого модуля сводится к реализации логики, управляющей каждым отдельным выходным сигналом посредством входных сигналов. Разберём принцип работы каждого выходного сигнала:
|
||||
Implementing any module comes down to implementing the logic that drives each individual output signal from the input signals. Let us examine the behavior of each output signal:
|
||||
|
||||
### mem_req_o, mem_we_o, mem_addr_o
|
||||
|
||||
Все эти сигналы подключаются напрямую к соответствующим core-сигналам:
|
||||
All of these signals are directly connected to their corresponding core signals:
|
||||
|
||||
- `mem_req_o` к `core_req_i`;
|
||||
- `mem_we_o` к `core_we_i`;
|
||||
- `mem_addr_o` к `core_addr_i`.
|
||||
- `mem_req_o` to `core_req_i`;
|
||||
- `mem_we_o` to `core_we_i`;
|
||||
- `mem_addr_o` to `core_addr_i`.
|
||||
|
||||
### mem_be_o
|
||||
|
||||
Данный сигнал является результатом мультиплексирования с управляющим сигналом `core_size_i`. Если `core_size_i` соответствует инструкции записи байта (`LDST_B`, 3'd0), то в сигнале `mem_be_o` бит с индексом равным значению двух младших бит адреса `core_addr_i` должен быть равен единице.
|
||||
This signal is the result of multiplexing controlled by the `core_size_i` signal. If `core_size_i` corresponds to a byte write instruction (`LDST_B`, 3'd0), then the bit of `mem_be_o` whose index equals the value of the two least significant bits of `core_addr_i` must be set to one.
|
||||
|
||||
Допустим, пришёл запрос на запись байта по адресу 18:
|
||||
For example, suppose a request arrives to write a byte at address 18:
|
||||
|
||||
- `core_req_i == 1`,
|
||||
- `core_we_i == 1`,
|
||||
- `core_size_i == LDST_B`
|
||||
- `core_addr_i == 32'b10010`
|
||||
|
||||
В данном случае, необходимо выставить единицу во втором (считая с нуля) бите сигнала `mem_be_o` (поскольку значение двух младших бит `core_addr_i` равно двум): `mem_be_o == 4'b0100`.
|
||||
In this case, bit 2 (counting from zero) of `mem_be_o` must be set to one (since the value of the two least significant bits of `core_addr_i` is two): `mem_be_o == 4'b0100`.
|
||||
|
||||
Если пришёл запрос на запись полуслова (`core_size_i == LDST_H`), то в сигнале `mem_be_o` необходимо выставить в единицу либо два старших, либо два младших бита (в зависимости от `core_addr_i[1]`: если `core_addr_i[1] == 1`, то в двух старших битах, если `core_addr_i[1] == 0`, то в двух младших).
|
||||
If a halfword write request arrives (`core_size_i == LDST_H`), then either the two most significant or the two least significant bits of `mem_be_o` must be set to one (depending on `core_addr_i[1]`: if `core_addr_i[1] == 1`, set the two most significant bits; if `core_addr_i[1] == 0`, set the two least significant bits).
|
||||
|
||||
Если пришёл запрос на запись слова (`core_size_i == LDST_W`), то в сигнале `mem_be_o` необходимо выставить в единицу все биты.
|
||||
If a word write request arrives (`core_size_i == LDST_W`), all bits of `mem_be_o` must be set to one.
|
||||
|
||||

|
||||
|
||||
_Рисунок 2. Временна́я диаграмма запросов на запись со стороны ядра и сигнала mem\_be\_o._
|
||||
_Figure 2. Waveform of core write requests and the mem\_be\_o signal._
|
||||
|
||||
### mem_wd_o
|
||||
|
||||
Сигнал `mem_wd_o` функционально связан с сигналом `mem_be_o`, т.к. они оба выполняют функцию записи конкретных байт в памяти. Допустим процессор хочет записать байт `0xA5` по адресу 18. Для этого он формирует сигналы:
|
||||
The `mem_wd_o` signal is functionally related to `mem_be_o`, as both serve the purpose of writing specific bytes to memory. Suppose the processor wants to write the byte `0xA5` to address 18. It generates the following signals:
|
||||
|
||||
- `core_req_i == 1`,
|
||||
- `core_we_i == 1`,
|
||||
@@ -139,68 +139,68 @@ _Рисунок 2. Временна́я диаграмма запросов на
|
||||
- `core_addr_i == 32'b10010`
|
||||
- `core_wd_i == 32h0000_00A5`
|
||||
|
||||
Мы уже знаем, что `mem_be_o` должен быть при этом равен `4'b0100`. Однако если в память придут сигналы:
|
||||
We already know that `mem_be_o` must equal `4'b0100` in this case. However, if the following signals arrive at memory:
|
||||
|
||||
- `mem_be_o == 4'b0100`,
|
||||
- `mem_wd_o == 32'h0000_00A5`
|
||||
|
||||
то по адресу 18 будет записано значение `0x00` (поскольку второй байт на шине `mem_wd_o` равен нулю).
|
||||
then the value `0x00` will be written to address 18 (because byte 2 on the `mem_wd_o` bus is zero).
|
||||
|
||||
Для того, чтобы по 18-ому адресу записалось значение `A5`, это значение должно оказаться во втором байте `mem_wd_o`. А в случае 17-го адреса, значение должно оказаться в первом байте и т.п.
|
||||
For the value `A5` to be written at address 18, that value must appear in byte 2 of `mem_wd_o`. For address 17, it must appear in byte 1, and so on.
|
||||
|
||||
Получается, что в случае записи байта, проще всего продублировать записываемый байт во все байты шины `mem_wd_o`, ведь в память запишется только тот, которому будет соответствовать бит `mem_be_o`, равный единице. Дублирование можно осуществить с помощью [конкатенации](../../Basic%20Verilog%20structures/Concatenation.md).
|
||||
Therefore, for a byte write, the simplest approach is to replicate the byte being written into all byte lanes of the `mem_wd_o` bus. Only the byte corresponding to the asserted bit in `mem_be_o` will actually be written to memory. Replication can be achieved using [concatenation](../../Basic%20Verilog%20structures/Concatenation.md).
|
||||
|
||||
В случае записи полуслова (`core_size_i == LDST_H`) ситуация схожа, только теперь дублировать надо не 1 байт 4 раза, а полслова (16 младших бит шины `core_wd_i`) два раза.
|
||||
For a halfword write (`core_size_i == LDST_H`), the approach is similar, except that instead of replicating 1 byte 4 times, the halfword (the 16 least significant bits of `core_wd_i`) is replicated twice.
|
||||
|
||||
В случае записи слова (`core_size_i == LDST_W`), сигнал `mem_wd_o` будет повторять сигнал `core_wd_i`.
|
||||
For a word write (`core_size_i == LDST_W`), `mem_wd_o` simply mirrors `core_wd_i`.
|
||||
|
||||

|
||||
|
||||
_Рисунок 3. Временна́я диаграмма запросов на запись со стороны ядра и сигнала mem\_wd\_o._
|
||||
_Figure 3. Waveform of core write requests and the mem\_wd\_o signal._
|
||||
|
||||
### core_rd_o
|
||||
|
||||
Сигнал `core_rd_o` — это сигнал, который будет содержать данные для записи в регистровый файл процессора во время инструкций загрузки из памяти (`LW`, `LH`, `LHU`, `LB`, `LBU`).
|
||||
The `core_rd_o` signal carries the data to be written to the processor's register file during load instructions (`LW`, `LH`, `LHU`, `LB`, `LBU`).
|
||||
|
||||
Предположим, по адресам `16-19` лежит слово `32'hA55A_1881` (см. _рис. 4_). Чтение по любому из адресов 16, 17, 18, 19 вернет это слово на входном сигнале `mem_rd_i`. В случае инструкции `LB` (чтение байта, который интерпретируется как знаковое число, во время которого `core_size_i == LDST_B`) по адресу 19, в регистровый файл должно быть записано значение `32'hFFFF_FFA5`, поскольку по 19-ому адресу лежит байт `A5`, который затем будет знакорасширен. В случае той же самой инструкции, но по адресу 18, в регистровый файл будет записано значение `32'h0000_005A` (знакорасширенный байт `5A`, расположенный по 18ому адресу).
|
||||
Suppose the word `32'hA55A_1881` is stored at addresses `16–19` (see _Fig. 4_). A read at any of addresses 16, 17, 18, or 19 will return this word on the `mem_rd_i` input signal. For the `LB` instruction (byte read interpreted as a signed number, where `core_size_i == LDST_B`) at address 19, the value `32'hFFFF_FFA5` must be written to the register file, since byte `A5` resides at address 19 and will be sign-extended. For the same instruction at address 18, the value `32'h0000_005A` will be written (the sign-extended byte `5A` located at address 18).
|
||||
|
||||
Получить нужный байт можно из входного сигнала `mem_rd_i`, но чтобы понять какие биты этого сигнала нас интересуют, необходимо посмотреть на входные сигналы `core_size_i` и `core_addr_i[1:0]`. `core_size_i` сообщит размер порции данных (сколько нужно взять байт из считанного слова), а `core_addr_i[1:0]` укажет номер байта в `mem_rd_i`, с которого эта порция данных начинается..
|
||||
The required byte can be extracted from the `mem_rd_i` input signal, but to determine which bits of that signal are relevant, it is necessary to examine `core_size_i` and `core_addr_i[1:0]`. `core_size_i` indicates the data size (how many bytes to extract from the read word), and `core_addr_i[1:0]` identifies the starting byte position within `mem_rd_i`.
|
||||
|
||||
В случае инструкции `LH` будет все тоже самое, только знакорасширяться будет не байт, а полуслово.
|
||||
For the `LH` instruction, the process is the same, except that a halfword is sign-extended rather than a byte.
|
||||
|
||||
А для инструкций `LBU` и `LHU` будет все тоже самое, только результат будет не знакорасширен, а дополнен нулями.
|
||||
For the `LBU` and `LHU` instructions, the process is the same, except that the result is zero-extended rather than sign-extended.
|
||||
|
||||
Для инструкций `LW` на выход `core_rd_o` пойдут данные `mem_rd_i` без изменений.
|
||||
For the `LW` instruction, `mem_rd_i` is passed to `core_rd_o` unchanged.
|
||||
|
||||

|
||||
|
||||
_Рисунок 4. Временна́я диаграмма запросов на чтение со стороны ядра и сигнала core\_rd\_o._
|
||||
_Figure 4. Waveform of core read requests and the core\_rd\_o signal._
|
||||
|
||||
### core_stall_o
|
||||
|
||||
Сигнал `core_stall_o` запрещает менять значение программного счётчика на время обращения в память. Этот сигнал должен:
|
||||
The `core_stall_o` signal prevents the program counter from advancing during a memory access. This signal must:
|
||||
|
||||
- стать равным единице в тот же такт, когда пришёл сигнал `core_req_i`
|
||||
- удерживать это значение до тех пор, пока не придет сигнал `mem_ready_i`, но не менее 1 такта (т.е. даже если сигнал `mem_ready_i` будет равен единице, `core_req_i` должен подняться хотя бы на 1 такт).
|
||||
- be asserted in the same clock cycle that `core_req_i` arrives;
|
||||
- remain asserted until `mem_ready_i` is received, but for no less than 1 clock cycle (i.e., even if `mem_ready_i` is already asserted, `core_stall_o` must be held high for at least 1 cycle).
|
||||
|
||||
Для реализации подобного функционала вам потребуется вспомогательный регистр `stall_reg`, каждый такт записывающий значение выхода `core_stall_o` и таблица истинности для этого выхода, представленная на _рис. 5_.
|
||||
To implement this behavior, you will need an auxiliary register `stall_reg` that captures the value of `core_stall_o` on every clock cycle, along with the truth table for this output shown in _Fig. 5_.
|
||||
|
||||

|
||||
|
||||
_Рисунок 5. Таблица истинности выхода `core_stall_o`._
|
||||
_Figure 5. Truth table for the `core_stall_o` output._
|
||||
|
||||
---
|
||||
|
||||
## Задание
|
||||
## Assignment
|
||||
|
||||
Реализовать блок загрузки и сохранения со следующим прототипом:
|
||||
Implement the load/store unit with the following prototype:
|
||||
|
||||
```Verilog
|
||||
module lsu(
|
||||
input logic clk_i,
|
||||
input logic rst_i,
|
||||
|
||||
// Интерфейс с ядром
|
||||
// Core interface
|
||||
input logic core_req_i,
|
||||
input logic core_we_i,
|
||||
input logic [ 2:0] core_size_i,
|
||||
@@ -209,7 +209,7 @@ module lsu(
|
||||
output logic [31:0] core_rd_o,
|
||||
output logic core_stall_o,
|
||||
|
||||
// Интерфейс с памятью
|
||||
// Memory interface
|
||||
output logic mem_req_o,
|
||||
output logic mem_we_o,
|
||||
output logic [ 3:0] mem_be_o,
|
||||
@@ -223,17 +223,17 @@ module lsu(
|
||||
|
||||

|
||||
|
||||
_Рисунок 6. Функциональная схема модуля `lsu`._
|
||||
_Figure 6. Functional block diagram of the `lsu` module._
|
||||
|
||||
---
|
||||
|
||||
### Порядок выполнения задания
|
||||
### Steps
|
||||
|
||||
1. Внимательно ознакомьтесь с описанием функционального поведения выходов **LSU**. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
2. Опишите модуль загрузки и сохранения с таким же именем и портами, как указано в задании
|
||||
1. При описании обратите внимание на то, что большая часть модуля является чисто комбинационной. В этом плане реализация модуля будет частично похожа на реализацию декодера.
|
||||
1. При описании мультиплексоров, управляемых сигналом core_size_i посредством конструкции `case`, не забывайте описать блок `default`, иначе вы получите защелку!
|
||||
2. Однако помимо комбинационной части, в модуле будет присутствовать и один регистр.
|
||||
3. Проверьте модуль с помощью верификационного окружения, представленного в файле [`lab_08.tb_lsu.sv`](lab_08.tb_lsu.sv). В случае, если в TCL-консоли появились сообщения об ошибках, вам необходимо [найти](../../Vivado%20Basics/05.%20Bug%20hunting.md) и исправить их.
|
||||
1. Перед запуском моделирования убедитесь, что у вас выбран корректный модуль верхнего уровня в `Simulation Sources`.
|
||||
4. Данная лабораторная работа не предполагает проверки в ПЛИС.
|
||||
1. Carefully review the description of the functional behavior of each **LSU** output. If any questions arise, consult your instructor.
|
||||
2. Implement the load/store unit module using the same name and ports as specified in the assignment.
|
||||
1. Note that the majority of the module is purely combinational logic. In this regard, the implementation will be partially similar to the decoder.
|
||||
1. When describing multiplexers controlled by `core_size_i` using a `case` statement, do not forget to include a `default` branch — otherwise you will infer a latch!
|
||||
2. In addition to the combinational logic, the module will also contain one register.
|
||||
3. Verify the module using the testbench provided in [`lab_08.tb_lsu.sv`](lab_08.tb_lsu.sv). If error messages appear in the TCL console, [locate](../../Vivado%20Basics/05.%20Bug%20hunting.md) and fix them.
|
||||
1. Before running the simulation, make sure the correct top-level module is selected in `Simulation Sources`.
|
||||
4. This lab does not require verification on an FPGA board.
|
||||
|
||||
Reference in New Issue
Block a user