From 00880bd3bd8f152c100f75cb2a2333e43ea56d9a Mon Sep 17 00:00:00 2001 From: Andrei Solodovnikov Date: Thu, 4 Jul 2024 16:44:45 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9B=D0=A08.=20=D0=94=D0=BE=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4?= =?UTF-8?q?=D0=B8=D1=87=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #18 --- .pic/Labs/lab_08_lsu/fig_02.wavedrom.svg | 4 ++ .pic/Labs/lab_08_lsu/fig_03.wavedrom.svg | 4 ++ .pic/Labs/lab_08_lsu/fig_04.wavedrom.svg | 4 ++ .../lab_08_lsu/{fig_02.png => fig_05.png} | Bin .../lab_08_lsu/{fig_02.xlsx => fig_05.xlsx} | Bin .../{fig_03.drawio.svg => fig_06.drawio.svg} | 0 Labs/08. Load-store unit/README.md | 45 +++++++++++------- 7 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 .pic/Labs/lab_08_lsu/fig_02.wavedrom.svg create mode 100644 .pic/Labs/lab_08_lsu/fig_03.wavedrom.svg create mode 100644 .pic/Labs/lab_08_lsu/fig_04.wavedrom.svg rename .pic/Labs/lab_08_lsu/{fig_02.png => fig_05.png} (100%) rename .pic/Labs/lab_08_lsu/{fig_02.xlsx => fig_05.xlsx} (100%) rename .pic/Labs/lab_08_lsu/{fig_03.drawio.svg => fig_06.drawio.svg} (100%) diff --git a/.pic/Labs/lab_08_lsu/fig_02.wavedrom.svg b/.pic/Labs/lab_08_lsu/fig_02.wavedrom.svg new file mode 100644 index 0000000..48547b1 --- /dev/null +++ b/.pic/Labs/lab_08_lsu/fig_02.wavedrom.svg @@ -0,0 +1,4 @@ + + + +clkcore_req_icore_we_icore_addr_i0b10000b10010b01000b01100b1100core_size_iLDST_BLDST_BLDST_HLDST_HLDST_Wmem_be_o0b00010b00100b00110b11000b1111 \ No newline at end of file diff --git a/.pic/Labs/lab_08_lsu/fig_03.wavedrom.svg b/.pic/Labs/lab_08_lsu/fig_03.wavedrom.svg new file mode 100644 index 0000000..fe090fd --- /dev/null +++ b/.pic/Labs/lab_08_lsu/fig_03.wavedrom.svg @@ -0,0 +1,4 @@ + + + +clkcore_req_icore_we_icore_addr_i0b10000b10010b01000b01100b1100core_size_iLDST_BLDST_BLDST_HLDST_HLDST_Wcore_wd_i000000A5F0A300C30000FACEC01DBABE50ADB70Bmem_be_o0b00010b00100b00110b11000b1111mem_wd_iA5A5A5A5C3C3C3C3FACEFACEBABEBABE50ADB70B \ No newline at end of file diff --git a/.pic/Labs/lab_08_lsu/fig_04.wavedrom.svg b/.pic/Labs/lab_08_lsu/fig_04.wavedrom.svg new file mode 100644 index 0000000..2b30953 --- /dev/null +++ b/.pic/Labs/lab_08_lsu/fig_04.wavedrom.svg @@ -0,0 +1,4 @@ + + + +clkcore_req_icore_we_icore_addr_i0b100110b100100b100110b100100b100000b10000core_size_iLDST_BLDST_BLDST_BULDST_HLDST_HLDST_Wmem_rd_iA55A1881A55A1881A55A1881A55A1881A55A1881A55A1881core_rd_oFFFFFFA50000005A000000A5FFFFA55A00001881A55A1881 \ No newline at end of file diff --git a/.pic/Labs/lab_08_lsu/fig_02.png b/.pic/Labs/lab_08_lsu/fig_05.png similarity index 100% rename from .pic/Labs/lab_08_lsu/fig_02.png rename to .pic/Labs/lab_08_lsu/fig_05.png diff --git a/.pic/Labs/lab_08_lsu/fig_02.xlsx b/.pic/Labs/lab_08_lsu/fig_05.xlsx similarity index 100% rename from .pic/Labs/lab_08_lsu/fig_02.xlsx rename to .pic/Labs/lab_08_lsu/fig_05.xlsx diff --git a/.pic/Labs/lab_08_lsu/fig_03.drawio.svg b/.pic/Labs/lab_08_lsu/fig_06.drawio.svg similarity index 100% rename from .pic/Labs/lab_08_lsu/fig_03.drawio.svg rename to .pic/Labs/lab_08_lsu/fig_06.drawio.svg diff --git a/Labs/08. Load-store unit/README.md b/Labs/08. Load-store unit/README.md index 9f14723..71dbf72 100644 --- a/Labs/08. Load-store unit/README.md +++ b/Labs/08. Load-store unit/README.md @@ -2,11 +2,9 @@ Итогом шестой лабораторной работы стал практически завершенный процессор архитектуры RISC-V. Особенностью той реализации процессора было отсутствие поддержки инструкций `LB`, `LBU`, `SB`, `LH`, `LHU`, `SH`. Тому было две причины: -- в третьей лабораторной работе была реализована память данных, не поддерживавшая возможность обновления отдельных байт; +- подключенный к памяти данных сигнал `byte_enable_i` был аппаратно зафиксирован на значении `4'b1111`, но на самом деле этим сигналом должен кто-то управлять; - необходимо подготовить считанные из памяти полуслова / байты для записи в регистровый файл. -В седьмой лабораторной работе была реализована новая память данных, в которой можно управлять записью в отдельные байты посредством управляющего сигнала `byte_enable`, каждый отдельный бит которого является разрешением записи в соответствующий байт слова. Необходимо управлять этим сигналом, используя интерфейс памяти процессора. - Для этих целей используется специальный модуль — **Блок загрузки и сохранения** (**Load and Store Unit**, **LSU**). ## Цель @@ -41,9 +39,11 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр На входной порт `core_addr_i` от процессора поступает адрес ячейки памяти, к которой будет произведено обращение. Намеренье процессора обратиться к памяти (и для чтения, и для записи) отражается выставлением сигнала `core_req_i` в единицу. Если процессор собирается записывать в память, то сигнал `core_we_i` выставляется в единицу, а сами данные, которые следует записать, поступают от него на вход `core_wd_i`. Если процессор собирается читать из памяти, то сигнал `core_we_i` находится в нуле, а считанные данные подаются для процессора на выход `core_rd_o`. -Инструкции `LOAD` и `STORE` в **RV32I** поддерживают обмен 8-битными, 16-битными или 32-битными значениями, однако в самом процессоре происходит работа только с 32-битными числами, поэтому загружая байты или полуслова из памяти их необходимо предварительно расширить до 32-битного значения. Для выбора разрядности на вход **LSU** подается сигнал `core_size_i`, принимающий следующие значения: +Инструкции `LOAD` и `STORE` в **RV32I** поддерживают обмен 8-битными, 16-битными или 32-битными значениями, однако в самом процессоре происходит работа только с 32-битными числами, поэтому загружая байты или полуслова из памяти их необходимо предварительно расширить до 32-битного значения. Расширять значения можно как нулями (если считанное число интерпретируется как беззнаковое), либо знаковым битом (если число интерпретируется как знаковое). Во время записи данных в память, они не расширяются, поскольку в основной памяти есть возможность записи отдельных байт. Таким образом, различать знаковые и беззнаковые числа необходимо только на этапе загрузки, но не сохранения. -| Название |Значение| Пояснение | +Для выбора разрядности и формата представления числа на вход **LSU** подается сигнал `core_size_i`, принимающий следующие значения (для удобства использования, данные значения определены в виде параметров в пакете `decoder_pkg`): + +| Параметр |Значение| Пояснение | |----------|--------|-------------------------------| | LDST_B | 3'd0 |Знаковое 8-битное значение | | LDST_H | 3'd1 |Знаковое 16-битное значение | @@ -51,8 +51,6 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр | LDST_BU | 3'd4 |Беззнаковое 8-битное значение | | LDST_HU | 3'd5 |Беззнаковое 16-битное значение | -Формат представления числа (является оно **знаковым** или **беззнаковым**) имеет значение только для операций типа `LOAD`: если число знаковое, то производится расширение знака до 32 бит, а если беззнаковое – расширение нулями. - Для операций типа `STORE` формат представления чисел не важен, для них `core_size_i` сможет принимать значение только от 0 до 2. Выходной сигнал `core_stall_o` нужен для остановки программного счетчика. Ранее логика этого сигнала временно находилась в модуле `riscv_unit` — теперь она займет свое законное место в модуле **LSU**. @@ -61,7 +59,7 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр В параграфе описывается организация внешней памяти, и то, как к ней подключается **LSU**. -Память данных имеет 32-битную разрядность ячейки памяти и поддерживает побайтовую адресацию. Это значит, что существует возможность записи значения по одному байту в пределах одного слова (4-байтовой ячейки памяти). Для указания на необходимые байты интерфейс к памяти предусматривает использование 4-битного сигнала `mem_be_o`, подаваемого вместе с адресом слова `mem_addr_o`. Позиции битов 4-битного сигнала соответствуют позициям байтов в слове. Если конкретный бит `mem_be_o` равен 1, то соответствующий ему байт будет записан в память. Данные для записи подаются на выход `mem_wd_o`. На результат чтения из памяти состояние `mem_be_o` не влияет, так как чтение производится всегда по 32-бита. +Память данных имеет 32-битную разрядность ячейки памяти и поддерживает побайтовую адресацию. Это значит, что существует возможность обновить любой байт пределах одного слова (4-байтовой ячейки памяти), не изменяя слова целиком. Для указания на обновляемые байты интерфейс к памяти предусматривает использование 4-битного сигнала `mem_be_o`, подаваемого вместе с адресом слова `mem_addr_o`. Позиции битов 4-битного сигнала соответствуют позициям байтов в слове. Если конкретный бит `mem_be_o` равен 1, то соответствующий ему байт в памяти будет обновлен. Данные для записи подаются на выход `mem_wd_o`. На результат чтения из памяти состояние `mem_be_o` не влияет, так как чтение производится всегда по 32-бита. После получения запроса на чтение/запись из ядра, **LSU** перенаправляет запрос в память данных, взаимодействие осуществляется следующими сигналами: @@ -116,7 +114,7 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр ### mem_be_o -По запросу на запись (`core_req_i == 1`, `core_we_i == 1`), если `core_size_i` соответствует инструкции записи байта (`SB`), то в сигнале `mem_be_o` бит с индексом равным значению двух младших бит адреса `core_addr_i` должен быть равен единице. +Данный сигнал принимает ненулевые значения только по запросу на запись (`core_req_i == 1`, `core_we_i == 1`), во время которого происходит мультиплексирование сигнала `core_size_i`. Если `core_size_i` соответствует инструкции записи байта (`LDST_B`, 3'd0), то в сигнале `mem_be_o` бит с индексом равным значению двух младших бит адреса `core_addr_i` должен быть равен единице. Допустим, пришел запрос на запись байта по адресу 18: @@ -127,9 +125,13 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр В данном случае, необходимо выставить единицу во втором (считая с нуля) бите сигнала `mem_be_o` (поскольку значение двух младших бит `core_addr_i` равно двум): `mem_be_o == 4'b0100`. -Если пришел запрос на запись полуслова (`SH`, `core_size_i == LDST_H`), то в сигнале `mem_be_o` необходимо выставить в единицу либо два старших, либо два младших бита (в зависимости от `core_addr[1]`) +Если пришел запрос на запись полуслова (`core_size_i == LDST_H`), то в сигнале `mem_be_o` необходимо выставить в единицу либо два старших, либо два младших бита (в зависимости от `core_addr[1]`: если `core_addr[1] == 1`, то в двух старших битах, если `core_addr[1] == 0`, то в двух младших). -Если пришел запрос на запись слова (`SW`, `core_size_i == LDST_W`), то в сигнале `mem_be_o` необходимо выставить в единицу все биты. +Если пришел запрос на запись слова (`core_size_i == LDST_W`), то в сигнале `mem_be_o` необходимо выставить в единицу все биты. + +![../../.pic/Labs/lab_08_lsu/fig_02.wavedrom.svg](../../.pic/Labs/lab_08_lsu/fig_02.wavedrom.svg) + +_Рисунок 2. Временна́я диаграмма запросов на запись со стороны ядра и сигнала mem\_be\_o._ ### mem_wd_o @@ -156,11 +158,15 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр В случае записи слова (`core_size_i == LDST_W`), сигнал `mem_wd_o` будет повторять сигнал `core_wd_i`. +![../../.pic/Labs/lab_08_lsu/fig_03.wavedrom.svg](../../.pic/Labs/lab_08_lsu/fig_03.wavedrom.svg) + +_Рисунок 3. Временна́я диаграмма запросов на запись со стороны ядра и сигнала mem\_wd\_o._ + ### core_rd_o Сигнал `core_rd_o` — это сигнал, который будет содержать данные для записи в регистровый файл процессора во время инструкций загрузки из памяти (`LW`, `LH`, `LHU`, `LB`, `LBU`). Чтобы понять, как управлять этим сигналом, нужно понять, что происходит во время этих инструкций. -Предположим, по адресам `16-19` лежит слово `32'hA55A_1881`. Чтение по любому из адресов 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ому адресу). +Предположим, по адресам `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ому адресу). Получить нужный байт можно из входного сигнала `mem_rd_i`, но чтобы понять какие биты этого сигнала нас интересуют, необходимо посмотреть на входные сигналы `core_size_i` и `core_addr_i[1:0]`. `core_size_i` сообщит конкретный тип инструкции (сколько нужно взять байт из считанного слова), а `core_addr_i[1:0]` укажет номер начального байта, который нужно взять из `mem_rd_i`. @@ -170,6 +176,10 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр Для инструкций `LW` на выход `core_rd_o` пойдут данные `mem_rd_i` без изменений. +![../../.pic/Labs/lab_08_lsu/fig_04.wavedrom.svg](../../.pic/Labs/lab_08_lsu/fig_04.wavedrom.svg) + +_Рисунок 4. Временна́я диаграмма запросов на чтение со стороны ядра и сигнала core\_rd\_o._ + ### core_stall_o Сигнал `core_stall_o` запрещает менять значение программного счетчика на время обращения в память. Этот сигнал должен: @@ -177,11 +187,11 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр - стать равным единице в тот же такт, когда пришел сигнал `core_req_i` - удерживать это значение до тех пор, пока не придет сигнал `mem_ready_i`, но не менее 1 такта (т.е. даже если сигнал `mem_ready_i` будет равен единице, `core_req_i` должен подняться хотя бы на 1 такт). -Для реализации подобного функционала вам потребуется вспомогательный регистр `stall_reg`, каждый такт записывающий значение выхода `core_stall_o` и таблица истинности для этого выхода, представленная на _рис. 2_. +Для реализации подобного функционала вам потребуется вспомогательный регистр `stall_reg`, каждый такт записывающий значение выхода `core_stall_o` и таблица истинности для этого выхода, представленная на _рис. 5_. -![../../.pic/Labs/lab_08_lsu/fig_02.png](../../.pic/Labs/lab_08_lsu/fig_02.png) +![../../.pic/Labs/lab_08_lsu/fig_05.png](../../.pic/Labs/lab_08_lsu/fig_05.png) -_Рисунок 2. Таблица истинности выхода `core_stall_o`._ +_Рисунок 5. Таблица истинности выхода `core_stall_o`._ --- @@ -215,9 +225,9 @@ module riscv_lsu( ``` -![../../.pic/Labs/lab_08_lsu/fig_03.drawio.svg](../../.pic/Labs/lab_08_lsu/fig_03.drawio.svg) +![../../.pic/Labs/lab_08_lsu/fig_05.drawio.svg](../../.pic/Labs/lab_08_lsu/fig_06.drawio.svg) -_Рисунок 3. Структурная схема модуля `riscv_lsu`._ +_Рисунок 6. Структурная схема модуля `riscv_lsu`._ --- @@ -228,6 +238,7 @@ _Рисунок 3. Структурная схема модуля `riscv_lsu`._ 1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `riscv_lsu.sv`. 2. Опишите в нем модуль `riscv_lsu` с таким же именем и портами, как указано в [задании](#задание). 1. При описании обратите внимание на то, что большая часть модуля является чисто комбинационной. В этом плане реализация модуля будет частично похожа на реализацию декодера. + 1. При описании мультиплексоров, управляемых сигналом core_size_i посредством конструкции `case`, не забывайте описать блок `default`, иначе вы получите защелку! 2. Однако помимо комбинационной части, в модуле будет присутствовать и один регистр. 3. После описания модуля, его необходимо проверить с помощью тестового окружения. 1. Тестовое окружение находится [здесь](tb_lsu.sv).