Compare commits

..

19 Commits

Author SHA1 Message Date
Andrei Solodovnikov
4676bf3b77 ЛР10-11. Обновление листинга 2026-07-25 16:41:01 +03:00
Andrei Solodovnikov
a64da6312b ЛР12. Исправление вывода в тб
В утверждении irq_cause_o_mcause при ошибке выводилось значение не из тех бит.

Fixes #165
2026-07-24 00:16:07 +03:00
Andrei Solodovnikov
bfb6bac9d5 ЛР8. Исправление тб
Из-за гонок не отлавливалась ошибка когда core_stall_o брался с выхода регистра
2026-07-22 14:52:47 +03:00
Andrei Solodovnikov
4aa7ed1be8 Исправление отображения svg (#164) 2026-07-01 14:33:40 +03:00
Andrei Solodovnikov
4313c22bee ЛР5. Стилистические правки 2026-07-01 14:29:23 +03:00
Andrei Solodovnikov
007889f849 ЛР4. Обновление тестбенча
Замена reg и wire, исправление нейминга сигналов, способов установки
задержек, а также уменьшен интервал вводимых чисел для уменьшения длины
временной диаграммы.
2026-07-01 14:29:23 +03:00
Andrei Solodovnikov
e09e48adff ЛР16. Исправление мейкфайла
Мейкфайл был написан из рассчета что startup.S будет в barebones,
но в методичке сказано скопировать его в корень кормарка.
2026-06-24 21:09:00 +03:00
Andrei Solodovnikov
8ffbc7a01b ЛР15. Исправление разрядности sw_i в тб 2026-06-18 13:17:12 +03:00
Andrei Solodovnikov
da422cfccb Исправление имени модуля в примере FAQ
Fix the instantiation of the adder module in the testbench.
2026-05-19 14:17:31 +03:00
Andrei Solodovnikov
71cb2f3099 Исправление имени модуля в примере common mistakes 2026-05-19 13:41:13 +03:00
Andrei Solodovnikov
63260f434e ЛР10. Исправление рисунка
Линии немного "съехали", функциональных изменений нет.
2026-05-07 15:59:11 +03:00
Andrei Solodovnikov
1034565000 WIP: сборка английской версии mdbook 2026-05-07 13:47:55 +03:00
ProshinaNastya
ccf769f48f ЛР5,7,9. Стилистические правки (#157)
* ЛР4. Удаление дублирования

* незначительные исправления README.md

* ЛР7. Исправление незначительных ошибок

* ЛР 9. Исправление пунктуации

* Apply suggestions from code review

Co-authored-by: Andrei Solodovnikov <VoultBoy@yandex.ru>

---------

Co-authored-by: Andrei Solodovnikov <VoultBoy@yandex.ru>
2026-04-25 10:39:17 +03:00
ProshinaNastya
44910a0b57 ЛР4. Удаление дублирования (#156) 2026-04-18 11:54:25 +03:00
Andrei Solodovnikov
a424d5c40f ЛР7. Исправление описки 2026-04-16 16:18:00 +03:00
Andrei Solodovnikov
896f7fccda ЛР10. Унификация заголовка 2026-04-14 09:46:01 +03:00
Andrei Solodovnikov
40074bcb25 ЛР2. Унификация заголовка 2026-04-14 09:45:33 +03:00
Andrei Solodovnikov
b480505e11 Исправление опечаток в документе "What is HDL" 2026-04-12 15:38:09 +03:00
Andrei Solodovnikov
59b569a1e4 Уточнение формулировки в документе "Как работает ПЛИС" 2026-04-12 14:13:08 +03:00
29 changed files with 696 additions and 2269 deletions

4
.github/book.toml vendored
View File

@@ -1,10 +1,12 @@
[book]
authors = ["Andrei Solodovnikov, Mikhail Popov"]
language = "ru"
multilingual = true
src = "./"
title = "Архитектуры процессорных систем"
[output.html]
git-repository-url = "https://github.com/MPSU/APS"
git-repository-icon = "fa-github"
site-url = "/APS/"
additional-js = ["theme/language-switcher.js"]
additional-css = ["theme/language-switcher.css"]

12
.github/book_en.toml vendored Normal file
View File

@@ -0,0 +1,12 @@
[book]
authors = ["Andrei Solodovnikov, Mikhail Popov"]
language = "en"
src = "./"
title = "Processor System Architectures"
[output.html]
git-repository-url = "https://github.com/MPSU/APS"
git-repository-icon = "fa-github"
site-url = "/APS/en/"
additional-js = ["theme/language-switcher.js"]
additional-css = ["theme/language-switcher.css"]

8
.github/prepare.sh vendored
View File

@@ -1,4 +1,10 @@
#!/bin/bash
set -e
mkdir src
mkdir -p src
cp .github/book.toml .github/*.md ./
# Copy custom theme assets (language switcher, etc.)
if [ -d ".github/theme" ]; then
cp -r .github/theme ./
fi

44
.github/theme/language-switcher.css vendored Normal file
View File

@@ -0,0 +1,44 @@
/* ── Language Switcher ───────────────────────────────────────────────────── */
#language-switcher {
display: inline-flex;
align-items: center;
gap: 4px;
background: none;
border: 1px solid var(--icons);
border-radius: 4px;
color: var(--icons);
cursor: pointer;
font-size: 0.82em;
font-weight: 600;
line-height: 1;
padding: 3px 8px;
/* sit nicely alongside the existing icon buttons */
margin-right: 4px;
vertical-align: middle;
transition: color 0.15s, border-color 0.15s, background 0.15s;
}
#language-switcher:hover,
#language-switcher:focus-visible {
color: var(--icons-hover);
border-color: var(--icons-hover);
background-color: rgba(128, 128, 128, 0.08);
outline: none;
}
/* Keep the flag emoji from being shrunk by the parent flex layout */
#language-switcher span[aria-hidden] {
font-size: 1.1em;
line-height: 1;
}
/* Hide text label on very small screens, show only flag */
@media (max-width: 420px) {
#language-switcher .lang-label {
display: none;
}
}

149
.github/theme/language-switcher.js vendored Normal file
View File

@@ -0,0 +1,149 @@
(function () {
"use strict";
const STORAGE_KEY = "book-language";
// ---------------------------------
// Current language from URL
// ---------------------------------
function currentLang() {
return /\/en\//.test(window.location.pathname) ? "en" : "ru";
}
// ---------------------------------
// Browser language
// ---------------------------------
function preferredLang() {
const langs = navigator.languages || [
navigator.language || "en",
];
const isRussian = langs.some((lang) =>
lang.toLowerCase().startsWith("ru")
);
return isRussian ? "ru" : "en";
}
// ---------------------------------
// URL conversion
// ---------------------------------
function pathForLang(lang) {
const path = window.location.pathname;
if (lang === "en") {
if (/\/en\//.test(path)) {
return path;
}
// /APS/foo → /APS/en/foo
return path.replace(/^(\/[^\/]+\/)/, "$1en/");
}
// lang === "ru"
// /APS/en/foo → /APS/foo
return path.replace("/en/", "/");
}
// ---------------------------------
// Switch language manually
// ---------------------------------
function switchLanguage() {
const newLang = currentLang() === "ru" ? "en" : "ru";
// Save explicit user choice
localStorage.setItem(STORAGE_KEY, newLang);
window.location.href =
pathForLang(newLang) +
window.location.search +
window.location.hash;
}
// ---------------------------------
// Automatic language selection
// ---------------------------------
function autoSelectLanguage() {
const current = currentLang();
// 1. User preference has priority
let desired = localStorage.getItem(STORAGE_KEY);
// 2. Otherwise use browser language
if (!desired) {
desired = preferredLang();
}
// Already correct
if (desired === current) {
return;
}
// Redirect
window.location.replace(
pathForLang(desired) +
window.location.search +
window.location.hash
);
}
// ---------------------------------
// UI
// ---------------------------------
function addSwitcher() {
const lang = currentLang();
const btn = document.createElement("button");
btn.id = "language-switcher";
btn.setAttribute(
"title",
lang === "ru"
? "Switch to English"
: "Переключить на русский"
);
btn.setAttribute("aria-label", btn.getAttribute("title"));
const icon = document.createElement("span");
icon.setAttribute("aria-hidden", "true");
icon.textContent = lang === "ru" ? "🇬🇧" : "🇷🇺";
const label = document.createElement("span");
label.className = "lang-label";
label.textContent = lang === "ru" ? "EN" : "RU";
btn.appendChild(icon);
btn.appendChild(label);
btn.addEventListener("click", switchLanguage);
const target = document.querySelector(".right-buttons");
if (target) {
target.insertBefore(btn, target.firstChild);
}
}
// ---------------------------------
// Init
// ---------------------------------
function init() {
autoSelectLanguage();
addSwitcher();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();

View File

@@ -1,55 +1,98 @@
# Sample workflow for building and deploying a mdBook site to GitHub Pages
# Builds the Russian (master) and English (english_version) editions of the
# mdBook and deploys them as one GitHub Pages site:
#
# To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html
# /APS/ ← Russian
# /APS/en/ ← English
#
name: Deploy mdBook site to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
branches: ["master", "english_version"]
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
env:
MDBOOK_VERSION: 0.4.36
steps:
- uses: actions/checkout@v4
# ── 1. Checkout master (Russian) ──────────────────────────────────────
- name: Checkout master (Russian)
uses: actions/checkout@v4
# checks out into $GITHUB_WORKSPACE (i.e. the working directory)
# ── 2. Install mdBook ─────────────────────────────────────────────────
- name: Install mdBook
run: |
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y
source "$HOME/.cargo/env"
cargo install --version "${MDBOOK_VERSION}" mdbook
# ── 3. Build Russian version ──────────────────────────────────────────
- name: Prepare Russian sources
run: |
chmod +x .github/prepare.sh
.github/prepare.sh
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh
rustup update
cargo install --version ${MDBOOK_VERSION} mdbook
- name: Build Russian (mdbook → ./book/)
run: |
source "$HOME/.cargo/env"
mdbook build
# ── 4. Checkout English branch ────────────────────────────────────────
- name: Checkout english_version branch
uses: actions/checkout@v4
with:
ref: english_version
path: english_src # checked out next to the working directory
# ── 5. Build English version ──────────────────────────────────────────
- name: Prepare English sources
run: |
cd english_src
chmod +x .github/prepare.sh
.github/prepare.sh
# Use the dedicated English book.toml (sets site-url = "/APS/en/")
cp .github/book_en.toml book.toml
# Copy language-switcher theme files from the master checkout
cp -r ../.github/theme ./theme
- name: Build English (mdbook → english_src/book/)
run: |
source "$HOME/.cargo/env"
cd english_src
mdbook build
# ── 6. Merge artifacts ────────────────────────────────────────────────
- name: Merge English build into Russian book/en/
run: |
mkdir -p book/en
cp -r english_src/book/. book/en/
# ── 7. Deploy ─────────────────────────────────────────────────────────
- name: Setup Pages
id: pages
uses: actions/configure-pages@v5
- name: Build with mdBook
run: mdbook build
- name: Upload artifact
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./book
# Deployment job
deploy:
environment:
name: github-pages

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 127 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 137 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 142 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 153 KiB

View File

@@ -32,7 +32,7 @@ endmodule
module testbench();
logic A, B, C;
adder DUT(
half_adder DUT(
.A(A), // <- здесь будет ошибка,
// т.к. в модуле half_adder нет порта 'A'
.b(B),

116
ERRATA.md
View File

@@ -2,6 +2,122 @@
![http://95.215.8.74:5000/days_since_last_commit.png](http://95.215.8.74:5000/days_since_last_commit.png)
**25.07.2026**: В _листинг II.10-1_ (стр. 179-181) были внесены изменения:
1. Адреса программного стека и стека прерываний изменены с `3FFC` и `1FFC` на `1F0` и `F0` соответственно.
Младшая часть адресов изменена с `C` на `0` поскольку верхушка стека должна быть выровнена по границе в 16 байт (таково требование соглашения о вызовах).
Старшая часть адреса была уменьшена с `3F` до `01` и с `1F` до `00`, чтобы оба стека уместились в исходную память данных, чей размер изначально был задан в `memory_pkg` репозитория на значении в 512 байт.
2. Поскольку адреса `1F0` и `F0` умещаются в 12 бит, загрузка этих адресов теперь умещается в одну инструкцию, а не две. Благодаря этому, инструкции `li`, расположенные по адресам `0x00` и `0x18` разворачиваются в одну инструкцию `addi`. Из-за этого изменились адреса всех инструкций в листинге.
3. В листинге была допущена ошибка: верхушка указателя на стек была записана в регистр `x2`, однако в последствии в качестве верхушки стека использовался регистр `x5`. Теперь листинг использует корректный регистр `x2`.
4. В начале обработчика перехвата `trap_handler` между инструкциями с адресами `0x44` и `0x48` была пропущена инструкция поднятия верхушки стека на 16 байт вверх `addi x2, x2, -16`. Аналогично, была пропущена инструкция опускания верхушки стека на 16 байт вниз `addi x2, x2, 16` в конце обработчика `done` между инструкциями с адресами `0x90` и `0x94` соответственно.
<details>
<summary> Исправленная версия листинга </summary>
```asm
_start:
# Инициализируем начальные значения регистров
00: li x2, 0x00001F0 # устанавливаем указатель на верхушку стека
04: li x3, 0x00000000 # устанавливаем указатель на глобальные данные
08: la x5, trap_handler # псевдоинструкция la аналогично li загружает число,
# только в случае la — это число является адресом
# указанного места (адресом обработчика перехвата)
0C: csrw mtvec, x5 # устанавливаем вектор прерывания
10: li x5, 0x000000F0 # готовим адрес верхушки стека прерывания
14: csrw mscratch, x5 # загружаем указатель на верхушку стека прерывания
18: li x5, -1 # подготавливаем маску, разрешающую все источники
# прерывания
1C: csrw mie, x5 # загружаем маску в регистр маски
20: li x5, 1 # начальное значение глобальной переменной
24: sw x5, 0(x3) # загружаем переменную в память
28: li x6, 0 # начальное значение, чтобы в симуляции не было xxx
2C: li x7, 0 # начальное значение, чтобы в симуляции не было xxx
# Вызов ecall исключительно из хулиганских соображений, поскольку в данной
# микроархитектурной реализации это приведет к появлению illegal_instr и
# последующей обработке исключения
30: ecall
# Вызов функции main
main:
34: beq x0, x0, main # бесконечный цикл, аналогичный while (1);
# ОБРАБОТЧИК ПЕРЕХВАТА
# Без стороннего вмешательства процессор никогда не перейдёт к инструкциям ниже,
# однако в случае перехвата в программный счетчик будет загружен адрес первой
# нижележащей инструкции.
# Сохраняем используемые регистры на стек
trap_handler:
38: csrrw x2, mscratch, x2 # меняем местами mscratch и x2
3C: addi x2, x2, -16 # поднимаем верхушку стека на 16 байт вверх
# (указатель на стек всегда должен быть выровнен
# по границе в 16 байт)
40: sw x6, 0(x2) # сохраняем x6 на стек mscratch
44: sw x7, 4(x2) # сохраняем x7 на стек mscratch
# Проверяем произошло ли прерывание
48: csrr x6, mcause # x6 = mcause
4C: li x7, 0x80000010 # загружаем в x7 код того, что произошло прерывание
50: # данная псевдоинструкция будет разбита на две
# инструкции: lui и addi
54: bne x6, x7, exc_handler # если коды не совпадают, переходим к проверке
# на исключение
# Обработчик прерывания
58: lw x7, 0(x3) # загружаем переменную из памяти
5C: addi x7, x7, 3 # прибавляем к значению 3
60: sw x7, 0(x3) # возвращаем переменную в память
64: j done # идем возвращать регистры и на выход
exc_handler: # Проверяем произошло ли исключение
68: li x7, 0x0000002 # загружаем в x7 код того, что произошло исключение
6C: bne x6, x7, done # если это не оно, то выходим
# Обработчик исключения
70: csrr x6, mepc # Узнаём значение PC (адреса инструкции,
# вызвавшей исключение)
74: lw x7, 0x0(x6) # Загружаем эту инструкцию в регистр x7.
# В текущей микроархитектурной реализации это
# невозможно, т.к. память инструкций отделена от
# памяти данных и не участвует в выполнении
# операций load / store.
# Другой способ узнать об инструкции, приведшей
# к исключению — добавить поддержку статусного
# регистра mtval, в который при исключении
# может быть записана текущая инструкция.
# Теоретически мы могли бы после этого
# сделать что-то, в зависимости от этой инструкции.
# Например, если это операция умножения — вызвать
# подпрограмму умножения.
78: addi x6, x6, 4 # Увеличиваем значение PC на 4, чтобы после
# возврата не попасть на инструкцию, вызвавшую
# исключение.
7C: csrw mepc, x6 # Записываем обновленное значение PC в регистр mepc
80: j done # идем восстанавливать регистры со стека и на выход
# Возвращаем регистры на места и выходим
done:
84: lw x6, 0(x2) # возвращаем x6 со стека
88: lw x7, 4(x2) # возвращаем x7 со стека
8C: addi x2, x2, 16 # опускаем верхушку стека обратно на 16 байт вниз
90: csrrw x2, mscratch, x2 # меняем обратно местами x2 и mscratch
94: mret # возвращаем управление программе (pc = mepc)
# что означает возврат в бесконечный цикл
```
</details>
<br>
**30.12.2025**: В Финальном обзоре ЛР№4 (стр. 111) указан неверный тип константы для инструкций переходов: 23-битная `const`, в то время как на самом деле используется 8-битная `offset`.
<details>

View File

@@ -1,279 +1,281 @@
# What is an FPGA and How Does It Work
# Что такое ПЛИС и как она работает
- [What is an FPGA and How Does It Work](#what-is-an-fpga-and-how-does-it-work)
- [History of FPGAs](#history-of-fpgas)
- [Digital Circuits and Logic Gates](#digital-circuits-and-logic-gates)
- [Digital Circuits](#digital-circuits)
- [Logic Gates](#logic-gates)
- [Multiplexers](#multiplexers)
- [Programmable Memory](#programmable-memory)
- [Look-Up Tables (LUTs)](#look-up-tables-luts)
- [D Flip-Flops](#d-flip-flops)
- [Arithmetic](#arithmetic)
- [Logic Blocks](#logic-blocks)
- [Interconnect Network](#interconnect-network)
- [Chapter Summary](#chapter-summary)
- [References](#references)
- [Что такое ПЛИС и как она работает](#Что-такое-ПЛИС-и-как-она-работает)
- [История появления ПЛИС](#История-появления-ПЛИС)
- [Цифровые схемы и логические вентили](#Цифровые-схемы-и-логические-вентили)
- [Цифровые схемы](#Цифровые-схемы)
- [Логические вентили](#Логические-вентили)
- [Мультиплексоры](#Мультиплексоры)
- [Программируемая память](#Программируемая-память)
- [Таблицы подстановки (Look-Up Tables, LUTs)](#Таблицы-подстановки-look-up-tables-luts)
- [D-триггеры](#d-триггеры)
- [Арифметика](#Арифметика)
- [Логические блоки](#Логические-блоки)
- [Сеть межсоединений](#Сеть-межсоединений)
- [Итоги главы](#Итоги-главы)
- [Список источников](#Список-источников)
> The sections "Digital Circuits and Logic Gates" and "Look-Up Tables" draw heavily from the article "[How Does an FPGA Work?[1]](https://learn.sparkfun.com/tutorials/how-does-an-fpga-work/all)" by `Alchitry, Ell C`, distributed under the [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license.
> Параграфы "Цифровые схемы и логические вентили" и "Таблицы подстановки" во многом используют материалы статьи "[How Does an FPGA Work?[1]](https://learn.sparkfun.com/tutorials/how-does-an-fpga-work/all)" за авторством `Alchitry, Ell C`, распространяемой по лицензии [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
## History of FPGAs
## История появления ПЛИС
Before integrated circuits existed, electronic devices were built using vacuum tubes, which performed amplification and switching functions. These tubes were bulky, power-hungry, and short-lived. They were later replaced by transistors, which became the foundation of modern electronic circuits. Initially, transistors were used as discrete components just like tubes, and circuits were assembled from them like a Lego model. If a mistake was made, it could be corrected by manually reworking the connections between components, much like fixing an error when building a Lego model.
До появления интегральных схем электронные устройства строились на базе вакуумных ламп, которые выполняли функции усиления и переключения. Эти лампы были громоздкими, энергозатратными и недолговечными. Затем их заменили на транзисторы, которые стали основой современных электронных схем. Поначалу транзисторы, как и лампы, использовались в виде отдельных компонентов, и схемы собирались из них, как модель из кубиков Lego. В случае ошибки её можно было исправить ручной корректировкой соединений между элементами, подобно исправлению ошибки при сборке модели Lego.
As technology advanced, transistors were miniaturized, making it possible to place them together with their interconnections on a single die. This gave rise to integrated circuits — electronic circuits fabricated on a semiconductor substrate and enclosed in a sealed package. This transition was a revolutionary step in the development of electronics, paving the way for compact and high-performance devices.
С развитием технологий произошла миниатюризация транзисторов, что позволило разместить их вместе с соединениями на одном кристалле. Так появились интегральные схемы — электронные схемы, выполненные на полупроводниковой подложке и заключённые в неразборный корпус. Этот переход стал революционным шагом в развитии электроники, открыв путь к созданию компактных и производительных устройств.
In most cases, it is impossible to correct a mistake made during the design and manufacture of an integrated circuit. Given that fabricating a prototype integrated circuit is a lengthy and expensive process (costing tens of thousands to millions of dollars), there arose a need for a flexible, fast, and inexpensive way to prototype and verify a circuit before committing to fabrication. This is how **programmable logic devices** (**PLDs**) came about.
It should be noted that this book mainly focuses on a specific type of PLD known as an FPGA (field-programmable gate array).
В большинстве случаев, исправить ошибку, допущенную при разработке и изготовлении интегральной схемы, невозможно. С учетом того, что изготовление прототипа интегральной схемы является долгим и затратным мероприятием (от десятков тысяч до миллионов долларов), возникла необходимость в гибком, быстром и дешёвом способе изготовления прототипа, и проверки на нём схемы до её изготовления. Так появились **программируемые логические интегральные схемы** (**ПЛИС**). В связи с повсеместным использованием англоязычной литературы, имеет смысл дать и англоязычное название этого класса устройств: **programmable logic devices** (**PLD**).
An FPGA contains a finite set of basic building blocks (primitives), primitive interconnect blocks, and input/output blocks. By applying a specific set of configuration data to an FPGA (**programming** it), the primitives, their interconnections with each other, and with the I/O blocks can be configured to implement a desired digital circuit. The advantage of an FPGA is that if an error is found in a prototype implemented on the FPGA, the digital circuit can be corrected and the FPGA reprogrammed.
Стоит оговориться, что в данной книге под термином ПЛИС будет подразумеваться конкретный тип программируемых схем: **FPGA** (**field-programmable gate array**, **программируемая пользователем вентильная матрица**, **ППВМ**).
In addition, FPGAs can be used effectively not only as a low-cost prototyping tool, but also as the final implementation vehicle for low-volume products (it is cheaper to buy and program an off-the-shelf batch of FPGAs than to fabricate a custom batch of chips).
ПЛИС содержит некоторое конечное множество базовых блоков (примитивов), блоки межсоединений примитивов и блоки ввода-вывода. Подав определенный набор воздействий на ПЛИС (**запрограммировав** её), можно настроить примитивы, их межсоединения между собой и блоками ввода-вывода, чтобы получить требуемую цифровую схему. Удобство ПЛИС заключается в том, что в случае обнаружения ошибки на прототипе, исполненном в ПЛИС, вы можете исправить свою цифровую схему и повторно запрограммировать ПЛИС.
Let us take a closer look at what this device is and how it works internally, but first we need to cover some basics of digital circuits and logic gates.
Кроме того, эффективно использовать ПЛИС не как средство дешевого прототипирования, но и как средство реализации конечного продукта в случае малого тиража (дешевле купить и запрограммировать готовую партию ПЛИС, чем изготовить партию собственных микросхем).
## Digital Circuits and Logic Gates
Давайте разберемся, что же это за устройство и как оно работает изнутри, но перед этим необходимо провести ликбез по цифровым схемам и логическим вентилям.
### Digital Circuits
## Цифровые схемы и логические вентили
A **digital circuit** is an **abstract model** of computation that operates on two discrete states, commonly denoted as `0` and `1`. It is important to understand that these states are not tied to specific physical quantities such as voltage in an electrical circuit. Instead, they represent generalized logical values that can be implemented on any technology capable of distinguishing two discrete states.
### Цифровые схемы
Thanks to this abstraction, digital circuits can be implemented not only using traditional electronic components, but also on entirely different platforms — for example, using [pneumatic systems](https://habr.com/ru/companies/ruvds/articles/692236/), [cardboard and marbles](https://habr.com/ru/articles/399391/), [redstone dust](https://minecraft.fandom.com/wiki/Tutorials/Redstone_computers) in Minecraft, or even through human interaction, as described in Liu Cixin's novel "The Three-Body Problem" (the efficiency of such implementations is another matter entirely). The core idea is that a digital circuit is decoupled from its physical implementation, focusing solely on the logic of interactions between the states `0` and `1`, which makes it universal and technology-independent.
**Цифровая схема** — это **абстрактная модель** вычислений, которая оперирует двумя дискретными состояниями, обычно обозначаемыми как `0` и `1`. Важно понимать, что эти состояния не привязаны к конкретным физическим величинам, таким как напряжение в электрической цепи. Вместо этого они представляют собой обобщенные логические значения, которые могут быть реализованы на любой технологии, способной различать два дискретных состояния.
Of course, when designing efficient digital circuits, the underlying technology must be taken into account.
Благодаря этой абстракции цифровые схемы могут быть реализованы не только с помощью традиционных электронных компонентов, но и на совершенно иных платформах, например на [пневматических системах](https://habr.com/ru/companies/ruvds/articles/692236/), [из картона и шариков](https://habr.com/ru/articles/399391/), [красной пыли](https://minecraft.fandom.com/wiki/Tutorials/Redstone_computers) в игре Майнкрафт или даже с использованием человеческого взаимодействия, подобно тому, как это описано в романе Лю Цысиня "Задача трёх тел" (эффективность подобных схем — это уже другой вопрос). Основная идея заключается в том, что цифровая схема отвязывается от физической реализации, фокусируясь лишь на логике взаимодействия состояний `0` и `1`, что делает её универсальной и независимой от конкретной технологии.
In electronics, the word "digital" describes circuits that abstract away from continuous (analog) voltage values, using only two discrete values: `0` and `1`. At this level of abstraction, we are not concerned with specific voltage levels or their thresholds, which allows us to design circuits in an idealized world where voltage can have only two values: `0` and `1`. Ensuring these conditions is the responsibility of the basic building blocks from which digital circuits are constructed.
Разумеется, при проектировании эффективных цифровых схем необходимо оглядываться на технологию, по которой эти схемы будут работать.
These basic building blocks are called **logic gates**.
В электронике словом "цифровая" описывают схемы, которые абстрагируются от непрерывных (аналоговых) значений напряжений, вместо этого используются только два дискретных значения: `0` и `1`. На данном уровне абстракции нас не интересуют конкретные значения напряжений и пороги этих значений, что позволяет нам разрабатывать схему в идеальном мире, где у напряжения может быть всего два значения: `0` и `1`. А обеспечением этих условий будут заниматься базовые блоки, из которых мы будем строить цифровые схемы.
### Logic Gates
Эти базовые блоки называются **логическими вентилями**.
There are many types of logic gates; we will consider four of them: **AND**, **OR**, **XOR**, and **NOT**. Each of these elements accepts a **digital value** as input (see [**Digital Circuits**](#digital-circuits)), performs a specific **logical function** on its inputs, and produces the result of that function as a **digital value** on its output.
### Логические вентили
The logic gates in _Figs. 14_ are illustrated using standard schematic symbols from two standards: **ANSI** and **GOST**. Since the ANSI variant is widely used in the literature, it will be used throughout this book.
Существует множество логических вентилей, мы рассмотрим четыре из них: **И**, **ИЛИ**, **Исключающее ИЛИ**, **НЕ**. Каждый из этих элементов принимает на вход **цифровое значение** (см. [**цифровая схема**](#Цифровые-схемы)), выполняет определенную **логическую функцию** над входами и подает на выход результат этой функции в виде **цифрового значения**.
The **AND** gate takes two inputs and outputs a value of `1` only when both inputs are `1`. If at least one input is `0`, the output is `0`. The **AND** gate is represented on schematics as follows:
Логические вентили на _рис. 1-4_ иллюстрируются условными графическими обозначениями (**УГО**), взятыми из двух стандартов: **ANSI** и **ГОСТ**. Ввиду повсеместного использования в литературе первого варианта, в дальнейшем в книге будет использован он.
Логический вентиль **И** принимает два входа и выдает на выход значение `1` только в том случае, если оба входа равны `1`. Если хотя бы один из входов `0`, то на выходе будет `0`. На схемах логический вентиль **И** отображается следующим образом:
![../.pic/Introduction/How%20FPGA%20works/fig_01.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_01.drawio.svg)
_Figure 1. Schematic symbol of the **AND** gate._
_Рисунок 1. УГО логического вентиля **И**._
The **OR** gate takes two inputs and outputs a value of `1` if at least one input equals `1`. If both inputs are `0`, the output is `0`. The **OR** gate is represented on schematics as follows:
Логический вентиль **ИЛИ** принимает два входа и выдает на выход значение `1` в случае, если хотя бы один из входов равен `1`. Если оба входа равны `0`, то на выходе будет `0`. На схемах логический вентиль **ИЛИ** отображается следующим образом:
![../.pic/Introduction/How%20FPGA%20works/fig_02.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_02.drawio.svg)
_Figure 2. Schematic symbol of the **OR** gate._
_Рисунок 2. УГО логического вентиля **ИЛИ**._
The **XOR** (Exclusive OR) gate takes two inputs and outputs a value of `1` when the input values are not equal to each other (one is `1` and the other is `0`). If both inputs are equal (both `0` or both `1`), the output is `0`. The **XOR** gate is represented on schematics as follows:
Логический вентиль **Исключающее ИЛИ** принимает два входа и выдает на выход значение `1` в случае, если значения входов не равны между собой (один из них равен `1`, а другой `0`). Если значения входов равны между собой (оба равны `0` или оба равны `1`), то на выходе будет `0`. На схемах логический вентиль **Исключающее ИЛИ** отображается следующим образом:
![../.pic/Introduction/How%20FPGA%20works/fig_03.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_03.drawio.svg)
_Figure 3. Schematic symbol of the **XOR** gate._
_Рисунок 3. УГО логического вентиля **Исключающее ИЛИ**._
The **NOT** gate is the simplest. It takes a single input and outputs its inverse. If the input is `0`, the output is `1`; if the input is `1`, the output is `0`. It is represented on schematics as follows:
Логический вентиль **НЕ** — самый простой. Он принимает один вход и подает на выход его инверсию. Если на вход пришло значение `0`, то на выходе будет `1`, если на вход пришло значение `1`, то на выходе будет `0`. Он обозначается на схемах следующим образом:
![../.pic/Introduction/How%20FPGA%20works/fig_04.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_04.drawio.svg)
_Figure 4. Schematic symbol of the **NOT** gate._
_Рисунок 4. УГО логического вентиля **НЕ**._
There are also variations of the basic gates, such as **NAND**, **NOR**, and **XNOR**, which differ from their base counterparts in that the result of the operation is inverted relative to the result of the corresponding operation without the inversion.
Также существуют вариации базовых вентилей, такие как **И-НЕ**, **ИЛИ-НЕ**, **Исключающее ИЛИ-НЕ**, отличающиеся от исходных тем, что результат операции инвертирован относительно результата аналогичной операции без **-НЕ**.
Logic gates can be built from **transistors**. A **transistor** is a device that can pass or block current depending on the voltage applied to its control terminal.
Логические вентили могут быть построены из **транзисторов**. **Транзистор** — это элемент, который может пропускать/блокировать ток в зависимости от поданного напряжения на его управляющий вход.
A key feature of modern integrated circuits is that they are built using complementary (**P**- and **N**-type) transistor pairs — **Complementary Metal-Oxide-Semiconductor** (**CMOS**) logic. For this type of transistor, it turns out to be more efficient to implement **NAND** and **NOR** operations.
Особенностью современных интегральных схем является то, что они строятся на основе комплементарной (взаимодополняющей) пары транзисторов **P** и **N**-типа (**Комплементарная Металл-Оксид-Полупроводниковая**, **КМОП** логика). Для данного типа транзисторов оказалось эффективнее реализовать операции **И-НЕ** и **ИЛИ-НЕ**.
From the perspective of building digital circuits, MOS transistors (**P**- and **N**-type) can be thought of as switches that either connect or disconnect two terminals. The difference between **P**- and **N**-type transistors lies in the voltage at the control terminal that causes the transistor to be "on" (terminals connected) or "off" (connection broken). _Fig. 5_ illustrates this difference.
С точки зрения построения цифровых схем МОП-транзисторы (**P**- и **N**-типа) можно воспринимать как выключатели, которые замыкают или размыкают связь между двумя выводами. Разница между **P**- и **N** типами заключается в значении напряжения на управляющем входе, при котором транзистор "открыт" (вход и выход замкнуты) или "закрыт" (связь разорвана). _Рис. 5_ иллюстрирует данное различие.
The terminals between which the connection is formed are called the "**drain**" (**d**) and "**source**" (**s**), and the control terminal is called the "**gate**" (**g**). Note that a **logic gate** and a transistor **gate** are different things!
Выводы, между которыми образуется связь называются "**сток**" (**drain**, **d**) и "**исток**" (**source**, **s**), а управляющий вход — "**затвор**" (**gate**, **g**). Обратите внимание, что логический вентиль (**logic gate**) и затвор транзистора (просто **gate**) — это разные сущности!
![../.pic/Introduction/How%20FPGA%20works/fig_05.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_05.drawio.svg)
_Figure 5. P-type and N-type MOS transistors._
_Рисунок 5. МОП-транзисторы P и N типа._
_Fig. 6_ shows how **NAND** and **NOR** gates are constructed using **CMOS** technology. Let us walk through the operation of the **NAND** gate.
На _рис. 6_ показан способ построения логических вентилей **И-НЕ**, **ИЛИ-НЕ** по **КМОП** технологии. Рассмотрим принцип работы вентиля **И-НЕ**.
Applying a value of `1` to input **A** or **B** turns on the corresponding N-channel transistor (shown in red in _Fig. 6_) and turns off its complementary P-channel transistor (shown in blue). Applying `1` to both inputs turns off both P-channel transistors (the upper portion of the circuit is open, effectively removing it from the output) and turns on both N-channel transistors. As a result, the output is connected to "ground" (the black triangle at the bottom of the circuit), which is equivalent to `0` in terms of digital values.
Подача значения `1` на вход **А** или **B** открывает соответствующий этому входу n-канальный транзистор (обозначен на _рис. 6_ красным цветом) и закрывает дополняющий его (комплементарный ему) p-канальный транзистор (обозначен синим цветом). Подача на оба входа `1` закрывает оба p-канальных транзистора (верхняя часть схемы разомкнута, что для значения на выходе означает, что её будто и нет) и открывает оба n-канальных транзистора. В результате чего выход замыкается на "землю" (чёрный треугольник внизу схемы), что эквивалентно `0` в контексте цифровых значений.
If at least one of the inputs **A** or **B** is `0`, one of the parallel-connected P-channel transistors turns on (while the connection to "ground" is broken), and the output is connected to the supply voltage (the two perpendicular lines at the top of the circuit), which is equivalent to `1` in terms of digital values.
В случае, если хотя бы на одном из входов **А** или **B** будет значение `0`, откроется один из параллельно соединенных p-канальных транзисторов (в то время как соединение с "землей" будет разорвано) и выход будет подключен к питанию (две перпендикулярные линии вверху схемы), что эквивалентно `1` в контексте цифровых значений.
As you can see, the output voltage is driven from the **power supply** or **ground**, not from the gate inputs themselves. This is precisely what ensures continuous voltage refresh and noise immunity of **digital circuits**.
Как вы видите, напряжение на выход подается от **источников постоянного питания** или **земли**, а не от входов вентиля, именно этим и обеспечивается постоянное обновление напряжения и устойчивость **цифровых схем** к помехам.
![../.pic/Introduction/How%20FPGA%20works/fig_06.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_06.drawio.svg)
_Figure 6. Schematic of **NAND** and **NOR** gates built from CMOS transistors._
_Рисунок 6. Схема логических вентилей **И-НЕ**, **ИЛИ-НЕ**, построенных на КМОП транзисторах._
As a rule, when it is necessary to invert an input or output of a logic element in a schematic, a small circle is drawn on it rather than adding an explicit **NOT** gate as shown in _Fig. 4_. For example, the **NAND** gate is represented in the form shown in _Fig. 6_.
Как правило, при необходимости инвертировать вход или выход логического элемента на схеме, на нем рисуют кружок вместо добавления логического вентиля **НЕ** в том виде, котором он изображён на _рис. 4_. К примеру, логический элемент **И-НЕ** обозначают в виде, представленном на _рис. 6_.
If desired, a **NAND** gate can easily be converted into an **AND** gate (just as a **NOR** gate can be converted into an **OR** gate) by placing an inverter at the output of **NAND**, built from two MOS transistors as shown in _Fig. 7_.
При желании, из логического элемента **И-НЕ** можно легко получить логический элемент **И** (как и элемент **ИЛИ** из **ИЛИ-НЕ**). Для этого необходимо поставить на выходе **И-НЕ** инвертор, собираемый из двух МОП-транзисторов по схеме, представленной на _рис. 7_.
![../.pic/Introduction/How%20FPGA%20works/fig_07.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_07.drawio.svg)
_Figure 7. Schematic of a **NOT** gate built from CMOS transistors._
_Рисунок 7. Схема логического вентиля **НЕ**, построенного на КМОП транзисторах._
CMOS logic is far from the only way to build digital elements; other approaches were widely used in the past, such as circuits built using only one type of transistor. However, the use of complementary pairs has proven to be the most efficient, and today this approach dominates digital circuit design.
КМОП логика далеко не единственный способ построения цифровых элементов, ранее достаточно широко применялись другие варианты построения схем, например только на одном типе транзисторов. Однако наиболее эффективным оказалось использование именно комплементарных пар, и на сегодня такой подход для цифровых схем является доминирующим.
Using only the logic gates described above, it is possible to build **any(!)** digital circuit.
Используя одни лишь описанные выше логические вентили можно построить **любую(!)** цифровую схему.
However, when describing digital circuits, certain digital blocks are used so frequently that dedicated symbols have been introduced for them — **adders**, **multipliers**, **multiplexers**, etc. — which are used when describing more complex circuits. Let us look at one of the fundamental building blocks in an FPGA: the **multiplexer**.
Однако, при описании цифровых схем, некоторые цифровые блоки используются настолько часто, что для них ввели отдельные обозначения (**сумматоры**, **умножители**, **мультиплексоры** т.п.), используемые при описании более сложных схем. Рассмотрим один из фундаментальных строительных блоков в ПЛИС — **мультиплексор**.
### Multiplexers
### Мультиплексоры
A multiplexer is a device that routes one of its input signals to the output, depending on the value of a **select signal**.
Мультиплексор — это устройство, которое в зависимости от значения **управляющего сигнала** подает на выход значение одного из входных сигналов.
The schematic symbol of a multiplexer is shown in _Figure 8_.
УГО мультиплексора представлено на _рисунке 8_.
![../.pic/Introduction/How%20FPGA%20works/fig_08.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_08.drawio.svg)
_Figure 8. Multiplexer symbol._
_Рисунок 8. Обозначение Мультиплексора._
The `/` symbol on the `sel` line indicates that this signal is multi-bit, and the number below specifies that the signal width is 6 bits.
Символ `/` на линии `sel` указывает на то, что данный сигнал является многоразрядным, а число ниже указывает на то, что разрядность сигнала составляет 6 бит.
A multiplexer can have any number of inputs, but always has exactly one output.
Число входов мультиплексора может быть различным, но выход у него всегда один.
**The way in which the select signal is encoded can also vary.** The simplest digital multiplexer circuit results from using [**one-hot**](https://en.wikipedia.org/wiki/One-hot) encoding. With this encoding, the value of the **multi-bit** select signal **always** contains **exactly one** `1`. The information carried by such an encoded signal is contained in the position of that `1` within the select signal.
**Способ, которым кодируется значение управляющего сигнала может также различаться**. Простейшая цифровая схема мультиплексора получится, если использовать [**унитарное**](https://ru.wikipedia.org/wiki/Унитарный_код) (**one-hot**) кодирование. При таком кодировании, значение **многоразрядного** управляющего сигнала **всегда** содержит **ровно одну** `1`. Информация, которую несёт закодированный таким образом сигнал содержится в положении этой `1` внутри управляющего сигнала.
Let us see how a multiplexer with a one-hot-encoded select signal can be implemented using only **AND** and **OR** gates:
Посмотрим, как можно реализовать мультиплексор с управляющим сигналом, использующим one-hot-кодирование, при помощи одних лишь логических вентилей **И**, **ИЛИ**:
![../.pic/Introduction/How%20FPGA%20works/fig_09.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_09.drawio.svg)
_Figure 9. Multiplexer implementation using one-hot encoding._
_Рисунок 9. Реализация мультиплексора, использующего one-hot кодирование._
If we set the select signal to `000010`, meaning that only the **first** bit of this signal (**counting from zero**) equals **one** (`sel[1] = 1`), we can see that a value of `0` is applied to one input of each **AND** gate. The exception is the **AND** gate for input `b`, which receives a value of `1`. This means that all **AND** gates (except the one connected to input `b`) will output `0` (see [Logic Gates](#logic-gates)) regardless of what is applied to inputs a, c, d, e, and f. The only input that will affect the circuit's output is `b`. When it is `1`, the corresponding **AND** gate outputs `1`. When it is `0`, the **AND** gate outputs `0`. In other words, the **AND** gate's output mirrors the value of `b`.
Если мы выставим значение управляющего сигнала, равное `000010`, означающее, что только **первый** бит этого сигнала (**счет ведется с нуля**) будет равен **единице** (`sel[1] = 1`), то увидим, что на один из входов каждого логического вентиля **И** будет подано значение `0`. Исключением будет логический вентиль **И** для входа `b`, на вход которого будет подано значение `1`. Это означает, что все логические вентили **И** (кроме первого, на который подается вход `b`) будут выдавать на выход `0` (см. [Логические вентили](#Логические-вентили)) вне зависимости от того, что было подано на входы a,c,d,e и f. Единственным входом, который будет влиять на работу схемы, окажется вход `b`. Когда он равен `1`, на выходе соответствующего логического вентиля **И** окажется значение `1`. Когда он равен `0` на выходе **И** окажется значение `0`. Иными словами, выход **И** будет повторять значение `b`.
![../.pic/Introduction/How%20FPGA%20works/fig_10.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_10.drawio.svg)
_Figure 10. Multiplexer implementation using one-hot encoding._
_Рисунок 10. Реализация мультиплексора, использующего one-hot кодирование._
The **OR** gate in this circuit has more than two inputs. Such a gate can be constructed as a cascade of two-input **OR** gates:
Логический вентиль **ИЛИ** на данной схеме имеет больше двух входов. Подобный вентиль может быть создан в виде каскада логических вентилей **ИЛИ**:
![../.pic/Introduction/How%20FPGA%20works/fig_11.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_11.drawio.svg)
_Figure 11. Multi-input **OR** gate implementation._
_Рисунок 11. Реализация многоходового логического **ИЛИ**._
A **multi-input OR gate** behaves exactly like a two-input one: it outputs `1` when at least one input is `1`. If all inputs are `0`, the output is `0`.
**Многовходовой вентиль ИЛИ** ведет себя ровно так же, как двухвходовой: он выдает на выход значение `1`, когда хотя бы один из входов равен `1`. В случае, если все входы равны `0`, на выход **ИЛИ** пойдет `0`.
In our multiplexer circuit, it is guaranteed that every input of the **OR** gate except one will be `0` (since every **AND** gate output except one will be `0`). This means the output of the **multi-input OR** gate depends on only **one** input (when `sel = 000010`, it depends on input `b`).
Для нашей схемы мультиплексора гарантируется, что каждый вход **ИЛИ** кроме одного будет равняться `0` (поскольку выход каждого **И** кроме одного будет равен `0`). Это означает, что выход **многовходового ИЛИ** будет зависеть только от **одного** входа (в случае, когда `sel = 000010` — от входа `b`).
![../.pic/Introduction/How%20FPGA%20works/fig_12.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_12.drawio.svg)
_Figure 12. Multiplexer implementation using one-hot encoding._
_Рисунок 12. Реализация мультиплексора, использующего one-hot кодирование._
By changing the value of `sel`, we can control which multiplexer input is routed to the output.
Меняя значение `sel`, мы можем управлять тем, какой из входов мультиплексора будет идти на его выход.
### Programmable Memory
### Программируемая память
Transistors can be used to build not only logic elements, but also memory elements. Fig. 13 shows a schematic of the simplest static memory cell, consisting of one transistor and two inverters (totaling 5 transistors, which is why it is called **5T** SRAM). This cell implements 1 bit of programmable memory and was one of the core components of the very first FPGA.
Из транзисторов можно построить не только логические элементы, но и элементы памяти. На рис. 13 представлена схема простейшей ячейки статической памяти, состоящей из транзистора и двух инверторов (т.е. суммарно состоящей из 5 транзисторов, поэтому она называется **5T** SRAM). Данная ячейка реализует 1 бит программируемой памяти, являвшейся одним из основных компонентов самой первой ПЛИС.
![../.pic/Introduction/Sequential%20logic/fig_06.drawio.svg](../.pic/Introduction/Sequential%20logic/fig_06.drawio.svg)
_Figure 13. Programmable memory cell of the Xilinx XC2064 FPGA [[2, p. 2-63](https://archive.org/details/programmablegate00xili/page/n93/mode/2up)]._
_Рисунок 13. Программируемая ячейка памяти ПЛИС Xilinx XC2064[[2, стр. 2-63](https://archive.org/details/programmablegate00xili/page/n93/mode/2up)]._
This memory is a **bistable cell** — a loop of two inverters in which the stored value is "latched". A signal inverted twice returns to its original value, and as it passes through each inverter, the signal's voltage level is refreshed, preventing it from decaying due to circuit resistance.
Данная память представляет собой **бистабильную ячейку** — петлю из двух инверторов, в которых "заперто" хранимое значение. Дважды инвертированный сигнал совпадает по значению с исходным, при этом, проходя через каждый из инверторов, сигнал обновляет свое значение напряжения, что не позволяет ему угаснуть из-за сопротивления цепи.
To write a new value into the bistable cell, an additional transistor is connected to its input, which connects or disconnects it from the supply voltage or ground.
Для того чтобы поместить в бистабильную ячейку новое значение, к её входу подключается еще один транзистор, замыкающий или размыкающий её с напряжением питания/земли.
## Look-Up Tables (LUTs)
## Таблицы подстановки (Look-Up Tables, LUTs)
Imagine a multiplexer with four input signals and a two-bit select signal (note that this signal now uses standard binary encoding). But instead of exposing the input signals to the outside world, let us connect them to programmable memory. This means we can "program" each input to hold a constant value. If we enclose the result in a separate block, we obtain a two-input **Look-Up Table** (**LUT**).
Представьте мультиплексор с четырьмя входными сигналами и двухбитным управляющим сигналом (обратите внимание, что теперь это сигнал использует обычное двоичное кодирование). Но теперь, вместо того чтобы выставлять входные сигналы во внешний мир, давайте подключим их к программируемой памяти. Это означает, что мы можем "запрограммировать" каждый из входов на какое-то константное значение. Поместим то, что у нас получилось, в отдельный блок и вот, мы получили двухвходовую **Таблицу подстановки** (**Look-Up Tables**, далее **LUT**).
![../.pic/Introduction/How%20FPGA%20works/fig_14.drawio.svg](../.pic/Introduction/How%20FPGA%20works/fig_14.drawio.svg)
_Figure 14. Look-Up Table (LUT) implementation._
_Рисунок 14. Реализация таблицы подстановки (Look-Up Table, LUT)._
The two inputs of the **LUT** are the bits of the select signal of the multiplexer hidden inside the **LUT**. By programming the multiplexer inputs (more precisely, by programming the memory to which the multiplexer inputs are connected), we can implement **any(!)** logical function with two inputs and one output on top of the **LUT**.
Эти два входа **LUT** являются битами управляющего сигнала мультиплексора, спрятанного внутри **LUT**. Программируя входы мультиплексора (точнее, программируя память, к которой подключены входы мультиплексора), мы можем реализовать на базе **LUT** **любую(!)** логическую функцию, принимающую два входа и возвращающую один выход.
For example, suppose we want to implement **logical AND**. To do so, we need to write the following content into the memory:
Допустим, мы хотим получить **логическое И**. Для этого, нам потребуется записать в память следующее содержимое:
| Address ({x, y}) | Value (f) |
|------------------|-----------|
|Адрес ({x, y}) | Значение (f)|
|---------------|-------------|
| 00 | 0 |
| 01 | 0 |
| 10 | 0 |
| 11 | 1 |
This is the simplest example — in practice, **LUT**s typically have more inputs, allowing them to implement more complex logic.
Это простейший пример — обычно **LUT**-ы имеют больше входов, что позволяет им реализовывать более сложную логику.
## D Flip-Flops
## D-триггеры
As you have already seen, using an unlimited number of LUTs, you can build a digital circuit that implements a logical function of any complexity. However, digital circuits are not limited to implementing logical functions only (circuits that implement logical functions are called **combinational circuits**, because the output depends only on the current combination of inputs). For example, a digital circuit implementing a processor cannot be built this way. Such circuits require memory elements. Note that we are not talking about the programmable memory used to configure the logic functions implemented by the LUTs — we are talking about memory cells used by the circuit's own logic.
Как вы уже поняли, используя неограниченное количество LUT-ов, вы можете построить цифровую схему, реализующую логическую функцию любой сложности. Однако цифровые схемы не ограничиваются реализацией одних только логических функций (цифровые схемы, реализующие логическую функцию, называются **комбинационными**, поскольку выход зависит только от комбинации входов). Например, так не построить цифровую схему, реализующую процессор. Для таких схем нужны элементы памяти. Заметим, что речь идет не о программируемой памяти, задавая значения которой мы управляем тем, какие логические функции будут реализовывать LUT-ы. Речь идет о ячейках памяти, которые будут использоваться логикой самой схемы.
The fundamental memory element is the **D flip-flop**. D flip-flops can be used to build other memory elements, such as **registers** (and registers can be used to build **random access memory** (**RAM**)), **shift registers**, and so on.
Такой базовой ячейкой памяти является **D-триггер** (**D flip-flop**). Из D-триггеров можно собирать другие ячейки памяти, например **регистры** (а из регистров можно собрать **память с произвольным доступом** (**random access memory**, **RAM**)), **сдвиговые регистры** и т.п.
A **D flip-flop** is a digital element capable of storing one bit of information. In its basic form, it has two inputs and one output. One input receives the value to be stored in the **D flip-flop**; the other controls the write operation (typically called `clk` or `clock`, and connected to the circuit's clock signal). When the control signal transitions from `0` to `1` (or from `1` to `0`, depending on the design), the data signal's value is captured into the **D flip-flop**. It is commonly said that a **D flip-flop** is built from two **D latches**, which in turn are built from **SR latches**. Ultimately, however, all of these elements can be implemented using **AND**/**OR** and **NOT** gates:
**D-триггер** — это цифровой элемент, способный хранить один бит информации. В базовом варианте у этого элемента есть два входа и один выход. Один из входов подает значение, которое будет записано в **D-триггер**, второй вход управляет записью (обычно он называется `clk` или `clock` и подключается к тактирующему синхроимпульсу схемы). Когда управляющий сигнал меняет своё значение с `0` на `1` (либо с `1` на `0`, зависит от схемы), в **D-триггер** записывается значение сигнала данных. Обычно, описывая **D-триггер**, говорится, что он строится из двух **триггеров-защелок** (**D-latch**), которые, в свою очередь, строятся из **RS-триггеров**. Однако в конечном итоге, все эти элементы могут быть построены на базе логических вентилей **И**/**ИЛИ**, **НЕ**:
![../.pic/Introduction/Sequential%20logic/fig_05.drawio.svg](../.pic/Introduction/Sequential%20logic/fig_05.drawio.svg)
_Figure 15. D flip-flop implementation._
_Рисунок 15. Реализация D-триггера._
## Arithmetic
## Арифметика
In addition to the blocks described above (multiplexers and the LUTs and registers built on top of them), there is another type of block so frequently used in digital circuits that it is pre-placed in FPGAs in large quantities: **arithmetic blocks**. These blocks are used for addition, subtraction, comparison, and counter implementations. Different FPGAs may include different arithmetic primitives: some include a 1-bit adder, others a carry-chain block.
Помимо описанных выше блоков (мультиплексоров и построенных на их основе LUT-ов и регистров) выделяется еще один тип блоков, настолько часто используемый в цифровых схемах, что его заранее размещают в ПЛИС в больших количествах: это арифметические блоки. Эти блоки используются при сложении, вычитании, сравнении чисел, реализации счётчиков. В разных ПЛИС могут быть предустановлены разные блоки: где-то это может быть 1-битный сумматор, а где-то блок вычисления ускоренного переноса (`carry-chain`).
All of these blocks can be implemented using logic gates. For example, here is how a full adder can be implemented:
Все эти блоки могут быть реализованы через логические вентили, например так можно реализовать сумматор:
![../.pic/Labs/lab_01_adder/fig_02.drawio.svg](../.pic/Labs/lab_01_adder/fig_02.drawio.svg)
_Figure 16. Full 1-bit adder implementation._
_Рисунок 16. Реализация полного однобитного сумматора._
## Logic Blocks
## Логические блоки
The previous sections covered individual types of digital blocks: look-up tables, registers, and arithmetic blocks. For structural convenience, these blocks are grouped together inside an FPGA as **logic blocks**. Typically, logic blocks in modern FPGAs consist of **logic cells** (or **logic elements**), but for simplicity we will use these terms interchangeably.
В предыдущих параграфах, были рассмотрены отдельные виды цифровых блоков: таблицы подстановок, регистры, арифметические блоки. Для удобства структурирования, эти блоки объединены в ПЛИС в виде **логических блоков**. Обычно, логические блоки современных ПЛИС состоят из **логических ячеек** (или **логических элементов**), но для простоты повествования, мы объединим все эти термины.
Логический блок может содержать одну или несколько **LUT**, **арифметический блок**, и один или несколько **D-триггеров**, которые соединены между собой некоторым количеством мультиплексоров. На _рисунке 17_ представлена схема того, как может выглядеть логический блок:
A logic block can contain one or more **LUT**s, an **arithmetic block**, and one or more **D flip-flops**, connected to each other through a number of multiplexers. _Figure 17_ shows what a logic block may look like:
![../.pic/Labs/lab_03_memory/fig_02.png](../.pic/Labs/lab_03_memory/fig_02.png)
_Figure 17. Logic cell schematic [[2]](https://en.wikipedia.org/wiki/Field-programmable_gate_array)._
_Рисунок 17. Схема логической ячейки[[2]](https://en.wikipedia.org/wiki/Field-programmable_gate_array)._
A logic block represents a chain of operations: `logical function implemented in LUT → arithmetic operation → write to D flip-flop`. Each multiplexer determines whether any of these stages is bypassed.
By configuring a logic block, the following variations of a circuit fragment can be obtained:
Логический блок представляет собой цепочку операций: `логическая функция, реализованная через LUT -> арифметическая операция -> Запись в D-триггер`. Каждый из мультиплексоров определяет то, будет ли пропущен какой-либо из этих этапов.
Таким образом, конфигурируя логический блок, можно получить следующие вариации кусочка цифровой схемы:
1. Combinational circuit (logical function implemented in a LUT)
2. Arithmetic operation
3. Write data to a D flip-flop
4. Combinational circuit with the result written to a D flip-flop
5. Arithmetic operation with the result written to a D flip-flop
6. Combinational circuit followed by an arithmetic operation
7. Combinational circuit followed by an arithmetic operation and a write to a D flip-flop
1. Комбинационная схема (логическая функция, реализованная в LUT)
2. Арифметическая операция
3. Запись данных в D-триггер
4. Комбинационная схема с записью результата в D-триггер
5. Арифметическая операция с записью результата в D-триггер
6. Комбинационная схема с последующей арифметической операцией
7. Комбинационная схема с последующей арифметической операцией и записью в D-триггер
_Figure 18_ shows a real example of logic block usage in the `xc7a100tcsg324-1` FPGA when implementing an Arithmetic Logic Unit (ALU) connected to the peripherals of the `Nexys-7` development board. In this figure, you can see the use of LUTs, an arithmetic block (carry-lookahead), and one of the D flip-flops. D flip-flops shown in grey are unused.
На _рисунке 18_ приведён реальный пример использования логического блока в ПЛИС `xc7a100tcsg324-1` при реализации Арифметико-логического устройства (АЛУ), подключенного к периферии отладочной платы `Nexys-7`. На этом рисунке вы можете увидеть использование LUT-ов, арифметического блока (ускоренного расчета переноса), и одного из D-триггеров. D-триггеры, обозначенные серым цветом, не используются.
![../.pic/Introduction/How%20FPGA%20works/fig_18.png](../.pic/Introduction/How%20FPGA%20works/fig_18.png)
_Figure 18. Example of logic cell usage._
_Рисунок 18. Пример использования логической ячейки._
With a large set of such logic blocks and the ability to interconnect them as needed, you have extensive capabilities for implementing virtually any digital circuit (the only limitation is the FPGA's capacity — i.e., the number of logic blocks, I/O pins, and so on).
Располагая большим наборов таких логических блоков, и имея возможность межсоединять их нужным вам образом, вы получаете широчайшие возможности по реализации практически любой цифровой схемы (ограничением является только ёмкость ПЛИС, т.е. количество подобных логических блоков, входов выходов и т.п.).
In addition to logic blocks, FPGAs also include other primitives: **block RAM**, **multiplier blocks**, and so on.
Помимо логических блоков в ПЛИС есть и другие примитивы: **блочная память**, **блоки умножителей** и т.п.
## Interconnect Network
## Сеть межсоединений
To understand how the interconnection of logic blocks is controlled, let us look at Fig. 19, taken from the FPGA patent [[4](https://patents.google.com/patent/US4870302A)].
Для того чтобы разобраться, как управлять межсоединением логических блоков, рассмотрим рис. 19, входящий в патент на ПЛИС[[4](https://patents.google.com/patent/US4870302A)].
![../.pic/Introduction/How%20FPGA%20works/fig_19.jpg](../.pic/Introduction/How%20FPGA%20works/fig_19.jpg)
_Figure 19. FPGA contents showing the interconnection of logic blocks and I/O blocks [[5](http://www.righto.com/2020/09/reverse-engineering-first-fpga-chip.html)]._
_Рисунок 19. Содержимое ПЛИС в виде межсоединения логических блоков и блоков ввода-вывода[[5](http://www.righto.com/2020/09/reverse-engineering-first-fpga-chip.html)]._
Nine logic blocks are shown in blue, and twelve I/O blocks in yellow. All of these blocks are surrounded by an **interconnect network**, which consists of a grid of horizontal and vertical wiring lines — the general purpose interconnect [[2, 2-66](https://archive.org/details/programmablegate00xili/page/n97/mode/2up)].
Синим показано 9 логических блоков, желтым — 12 блоков ввода-вывода. Все эти блоки окружены **сетью межсоединений** (interconnect net), представляющей собой решётку из горизонтальных и вертикальных соединительных линий — межсоединений общего назначения (general purpose interconnect) [[2, 2-66](https://archive.org/details/programmablegate00xili/page/n97/mode/2up)].
The diagonal marks at line crossings represent **programmable interconnect points** (**PIPs**): transistors whose gates are connected to programmable memory.
Косыми чертами в местах пересечения линий обозначены **программируемые точки межсоединений** (**programmable interconnect points**, **PIP**s), представляющие собой транзисторы, затвор которых подключен к программируемой памяти.
By controlling the value stored in the memory connected to a transistor's gate, it is possible to determine whether that transistor acts as an open connection or a closed switch at that point in the grid. This allows "unused" segments of the network to be removed, leaving only the routes needed to connect the logic blocks to each other.
Управляя значением в подключенной к затвору транзистора памяти, можно управлять тем, что из себя будет представлять транзистор в данной точке: разрыв, или цепь. А значит, можно удалять "лишние" участки сети, оставляя только используемые логические блоки, соединенные между собой.
## Chapter Summary
## Итоги главы
1. Using elements such as **transistors**, it is possible to build **logic gates**: **AND**, **OR**, **NOT**, and so on.
2. Using **logic gates**, it is possible to create circuits that implement both **logical functions** (**combinational circuits**) and complex logic with memory (**sequential circuits**).
3. Logic gates can also be used to build the important combinational circuit known as the **multiplexer**: a digital block that routes one of its input signals to the output based on the value of a select signal.
4. Furthermore, by connecting the input of a bistable cell (a loop of two inverters) to a transistor, one bit of **programmable memory** can be obtained.
5. By connecting the input signals of a multiplexer to programmable memory, a **Look-Up Table** (**LUT**) is obtained, which can implement simple logical functions. LUTs can replace AND/OR/NOT gates and have the advantage of being dynamically reconfigurable. Logic gates, by contrast, are fixed at fabrication and cannot be changed afterwards.
6. Logic gates can also be used to build the basic memory element — the **D flip-flop** — and the combinational circuit known as the **full 1-bit adder** (or any other commonly used arithmetic block).
7. Combining a LUT, an arithmetic block, and a D flip-flop yields the FPGA structure known as a **logic block**.
8. A logic block (as well as other **primitives**, such as **block RAM** or **multipliers**) represents a set of blocks that are physically pre-placed in the FPGA die; their quantity is fixed by the specific FPGA and cannot be changed.
9. By connecting programmable memory to transistors located at the nodes of the **interconnect network**, the placement of breaks in the network can be controlled, making it possible to leave only the routing paths that carry signals where they are needed (**signal routing**).
10. By **configuring primitives** and **routing signals** between them (see item 4), **virtually any digital circuit** can be implemented (subject to the FPGA's capacity constraints).
1. Используя такие элементы, как **транзисторы**, можно собирать **логические вентили**: элементы **И**, **ИЛИ**, **НЕ** и т.п.
2. Используя **логические вентили**, можно создавать схемы, реализующие как **логические функции** (**комбинационные схемы**), так и сложную логику с памятью (**последовательностные схемы**).
3. Из логических вентилей строится и такая важная комбинационная схема, как **мультиплексор**: цифровой блок, который в зависимости от значения управляющего сигнала подаёт на выход один из входных сигналов.
4. Кроме того, подключив вход бистабильной ячейки (представляющую собой петлю из двух инверторов) к транзистору, можно получить 1 бит **программируемой памяти**.
5. Подключив входные сигналы мультиплексора к программируемой памяти, можно получить **Таблицу подстановок** (**Look-Up Table**, **LUT**), которая может реализовывать простейшие логические функции. LUT-ы позволяют заменить логические вентили И/ИЛИ/НЕ, и удобны тем, что их можно динамически изменять. Логические вентили в свою очередь исполняются на заводе и уже не могут быть изменены после создания.
6. Из логических вентилей также можно собрать базовую ячейку памяти: **D-триггер**, и такую комбинационную схему как **полный 1-битный сумматор** (или любой другой часто используемый арифметический блок).
7. Объединив LUT, арифметический блок и D-триггер получается структура в ПЛИС, которая называется **логический блок**.
8. Логический блок (а также другие **примитивы**, такие как **блочная память** или **умножители**) — это множество блоков, которые заранее физически размещаются в кристалле ПЛИС, их количество строго определено конкретной ПЛИС и не может быть изменено.
9. Подключая программируемую память к транзисторам, расположенных в узлах **сети межсоединений**, можно управлять расположением разрывов в сети, а значит можно оставить только маршрут, по которому сигнал пойдет туда, куда нам нужно (**трассировать сигнал**).
10. **Конфигурируя примитивы** и **трассируя сигнал** между ними (см. п.4), можно получить **практически любую цифровую схему** (с учетом ограничения ёмкости ПЛИС).
## References
## Список источников
1. Alchitry, Ell C / [How Does an FPGA Work?](https://learn.sparkfun.com/tutorials/how-does-an-fpga-work/all)
2. Xilinx / [The Programmable Gate Array Data Book](https://archive.org/details/programmablegate00xili)

View File

@@ -1,83 +1,83 @@
# What is a Hardware Description Language (HDL)
# Что такое язык описания аппаратуры (HDL)
In the early days of digital electronics, [digital circuits](How%20FPGA%20works.md#Digital-circuits) drawn on paper were small, while their physical hardware implementations were large. As electronics evolved into microelectronics, the paper diagrams grew increasingly complex, while the physical size of their chip implementations shrank dramatically. In _Fig. 1_, you can see the digital circuit of the Intel 4004 processor, released in 1971.
На заре появления цифровой электроники, [цифровые схемы](How%20FPGA%20works.md#Цифровые-схемы) в виде диаграммы на бумаге были маленькими, а их реализация в виде физической аппаратуры — большой. В процессе развития электроники (и её преобразования в микроэлектронику) цифровые схемы на бумаге становились всё больше, а относительный размер их реализации в виде физических микросхем — всё меньше. На _рис. 1_, вы можете увидеть цифровую схему устройства Intel 4004, выпущенного в 1971 году.
![../.pic/Introduction/What%20is%20HDL/i4004.gif](../.pic/Introduction/What%20is%20HDL/i4004.gif)
_Figure 1. Digital circuit of the Intel 4004 processor at the transistor level [[1]](https://www.4004.com/mcs4-masks-schematics-sim.html)._
_Рисунок 1. Цифровая схема процессора Intel 4004 на уровне транзисторов[[1]](https://www.4004.com/mcs4-masks-schematics-sim.html)._
This chip contains 2,300 transistors [[2]](https://en.wikipedia.org/wiki/Intel_4004).
Данная микросхема состоит из 2300 транзисторов[[2]](https://en.wikipedia.org/wiki/Intel_4004).
Over the past fifty years, the complexity of digital circuits has grown enormously. Modern desktop processors contain tens of billions of transistors. If printed at its original scale, the diagram above would occupy a rectangle of 115×140 cm, with an area of about 1.6 m<sup>2</sup>. Assuming that the print area scales linearly with the transistor count, printing the circuit of a modern processor with 23 billion transistors would require an area of 16 million m<sup>2</sup> — equivalent to a square with a side length of 4 km.
За прошедшие полсотни лет сложность цифровых схем выросла колоссально. Современные процессоры для настольных компьютеров состоят из десятков миллиардов транзисторов. Диаграмма выше при печати в оригинальном размере займет прямоугольник размером 115х140 см с площадью около 1.6 м<sup>2</sup>. Предполагая, что площадь печати имеет прямо пропорциональную зависимость от количества транзисторов, получим, что распечатка схемы современного процессора из 23 млрд транзисторов заняла бы площадь в 16млнм<sup>2</sup>, что эквивалентно квадрату со стороной в 4км.
<img src="../.pic/Introduction/What%20is%20HDL/ancient_city.png" alt="Ancient city" width="400"/>
<img src="../.pic/Introduction/What%20is%20HDL/ancient_city.png" alt="Старый город" width="400"/>
_Figure 2. The scale that digital circuit diagrams of modern processors would reach if printed on paper._
_Рисунок 2. Масштаб размеров, которых могли бы достигать цифровые схемы современных процессоров, если бы они печатались на бумаге._
As you might guess, at some point between 1971 and 2024, engineers stopped designing digital circuits by drawing them on paper.
Как вы можете догадаться, в какой-то момент между 1971-м и 2024-м годами инженеры перестали разрабатывать цифровые схемы, рисуя их на бумаге.
Of course, when designing a device, it is not necessary to draw every transistor on a schematic — complexity can be managed by moving between levels of abstraction. For example, one can start designing at the level of functional blocks, and then draw the schematic for each individual block.
Разумеется, разрабатывая устройство, не обязательно вырисовывать на схеме каждый транзистор — можно управлять сложностью, переходя с одного уровня абстракции на другой. Например, начинать разработку схемы с уровня функциональных блоков, а затем рисовать схему для каждого отдельного блока.
For instance, the Intel 4004 circuit can be represented as follows:
К примеру, схему Intel 4004 можно представить в следующем виде:
<img src="../.pic/Introduction/What%20is%20HDL/4004_arch.png" alt="../.pic/Introduction/What%20is%20HDL/4004_arch.png" width="500"/>
_Figure 3. Digital circuit of the Intel 4004 processor at the functional block level [[2]](https://en.wikipedia.org/wiki/Intel_4004)._
_Рисунок 3. Цифровая схема процессора Intel 4004 на уровне функциональных блоков[[2]](https://en.wikipedia.org/wiki/Intel_4004)._
However, even individual blocks can sometimes be quite complex. Consider the hardware AES encryption block [[3]](https://csrc.nist.gov/files/pubs/fips/197/final/docs/fips-197.pdf) shown in Figure 4:
Однако несмотря на это, даже отдельные блоки порой бывают довольно сложны. Возьмем блок аппаратного шифрования по алгоритму AES[[3]](https://csrc.nist.gov/files/pubs/fips/197/final/docs/fips-197.pdf) на рисунке 4:
![../.pic/Introduction/What%20is%20HDL/aes_enc_sml.png](../.pic/Introduction/What%20is%20HDL/aes_enc_sml.png)
_Figure 4. Digital circuit of a hardware AES encryption block [[4]](https://iis-people.ee.ethz.ch/~kgf/acacia/acacia_thesis.pdf)._
_Рисунок 4. Цифровая схема блока аппаратного шифрования по алгоритму AES[[4]](https://iis-people.ee.ethz.ch/~kgf/acacia/acacia_thesis.pdf)._
Note that even this block is not shown at the individual transistor level. Each XOR operation, multiplication, signal multiplexing, and substitution table is a separate block whose functionality still needs to be implemented.
At some point, engineers realized it was simpler to describe a digital circuit in textual form rather than graphical form.
Заметьте, что даже этот блок не представлен на уровне отдельных транзисторов. Каждая операция Исключающего ИЛИ, умножения, мультиплексирования сигнала и таблицы подстановки — это отдельные блоки, функционал которых ещё надо реализовать.
В какой-то момент инженеры поняли, что проще описать цифровую схему в текстовом представлении, нежели в графическом.
How can a digital circuit be described in text? Let us consider the digital circuit of a half-adder:
Как можно описать цифровую схему текстом? Рассмотрим цифровую схему полусумматора:
![Half-adder schematic](../.pic/Introduction/What%20is%20HDL/fig_05.drawio.svg)
![Схема полусумматора](../.pic/Introduction/What%20is%20HDL/fig_05.drawio.svg)
_Figure 5. Digital circuit of a half-adder at the logic gate level._
_Рисунок 5. Цифровая схема полусумматора на уровне логических вентилей._
This **module** (_half-adder_) has two **inputs**: _a_ and _b_, and two **outputs**: _sum_ and _carry_.
The _sum_ output is the **result** of the logical **XOR** operation on operands _a_ and _b_.
The _carry_ output is the **result** of the logical **AND** operation on operands _a_ and _b_.
Это **устройство** (_полусумматор_) имеет два **входа**: _a_ и _b_, а также два **выхода**: _sum_ и _carry_.
Выход _sum_ является **результатом** логической операции **Исключающее ИЛИ** от операндов _a_ и _b_.
Выход _carry_ является **результатом** логической операции **И** от операндов _a_ и _b_.
The text above is precisely the description from which this digital circuit can be reconstructed. If the description is standardized, only the words in bold and italics need to be retained. The following is an example of how this circuit could be described according to the IEEE 1364-2005 standard (the Verilog Hardware Description Language — HDL):
Текст выше и является тем описанием, по которому можно воссоздать эту цифровую схему. Если стандартизировать описание схемы, то в нем можно будет оставить только слова, выделенные жирным и курсивом. Пример того, как можно было бы описать эту схему по стандарту IEEE 1364-2005 (язык описания аппаратуры (Hardware Description Language — HDL) Verilog):
```Verilog
module half_sum( // module half-adder with
input a, // input a,
input b, // input b,
output sum, // output sum and
output carry // output carry.
module half_sum( // устройство полусумматор cо
input a, // входом a,
input b, // входом b,
output sum, // выходом sum и
output carry // выходом carry.
);
assign sum = a ^ b; // Where output sum is the result of XOR of a and b,
assign carry = a & b; // and output carry is the result of logical AND of a and b.
assign sum = a ^ b; // Где выход sum является результатом Исключающего ИЛИ от a и b,
assign carry = a & b; // а выход carry является результатом логического И от a и b.
endmodule
```
At first glance, this description appears even longer than the natural-language version. However, the extra length comes only from line breaks and some redundancy in the port declarations, which was added to improve readability. The same description could also be written as:
На первый взгляд такое описание выглядит даже больше, чем записанное естественным языком, однако видимый объем получен только за счёт переноса строк и некоторой избыточности в описании входов и выходов, которая была добавлена для повышения читаемости. То же самое описание можно было записать и в виде:
```Verilog
``` Verilog
module half_sum(input a, b, output sum, carry);
assign sum = a ^ b;
assign carry = a & b;
endmodule
```
It is important to note that Verilog code describes the device as a whole, all at once. It's a **description of a circuit**, not **line-by-line program execution**.
Важно отметить, что код на языке Verilog описывает устройство целиком, одномоментно. Это **описание схемы** выше, а **не построчное выполнение программы**.
With practice, describing a circuit in text becomes much easier and requires no graphical representation. A specification — a formal statement of how the device should behave — is sufficient. From it, an algorithm is developed, which is then expressed as an HDL description.
С практикой описание схемы в текстовом виде становится намного проще и не требует графического представления. Для описания достаточно только спецификации: формальной записи того, как должно работать устройство. По ней разрабатывается алгоритм, который затем претворяется в описание на HDL.
An interesting fact: it was mentioned earlier that engineers stopped designing devices by drawing digital circuits sometime between 1971 and 2024. The first conference dedicated to hardware description languages was held in 1973 [[5, p. 8]](https://dl.acm.org/doi/pdf/10.1145/3386337). Thus, the Intel 4004 can be considered one of the last digital devices designed without the use of hardware description languages.
Занятный факт: ранее было высказано предположение о том, что инженеры перестали разрабатывать устройства, рисуя цифровые схемы в промежуток времени между 1971-м и 2024-м годами. Так вот, первая конференция, посвящённая языкам описания аппаратуры состоялась в 1973-м году[[5, стр.8]](https://dl.acm.org/doi/pdf/10.1145/3386337). Таким образом, Intel 4004 можно считать одним из последних цифровых устройств, разработанных без использования языков описания аппаратуры.
## References
## Список источников
1. [Intel 4004 — 50th Anniversary Project](https://www.4004.com/mcs4-masks-schematics-sim.html);
2. [Wikipedia page on Intel 4004](https://en.wikipedia.org/wiki/Intel_4004);
3. [F. Kağan Gürkaynak / Side Channel Attack Secure Cryptographic Accelerators](https://iis-people.ee.ethz.ch/~kgf/acacia/acacia_thesis.pdf);
2. [Страница википедии по Intel 4004](https://en.wikipedia.org/wiki/Intel_4004);
3. [F.Kağan. Gürkaynak / Side Channel Attack Secure Cryptographic Accelerators](https://iis-people.ee.ethz.ch/~kgf/acacia/acacia_thesis.pdf);
4. [FIPS 197, Advanced Encryption Standard (AES)](https://csrc.nist.gov/files/pubs/fips/197/final/docs/fips-197.pdf);
5. [P. Flake, P. Moorby, S. Golson, A. Salz, S. Davidmann / Verilog HDL and Its Ancestors and Descendants](https://dl.acm.org/doi/pdf/10.1145/3386337).

View File

@@ -12,7 +12,7 @@
- способами описания [мультиплексора](../../Basic%20Verilog%20structures/Multiplexors.md) на языке SystemVerilog.
## Общий ход выполнения работы
## Ход работы
1. Изучить устройство и принцип работы АЛУ (раздел [#теория](#Теория))
2. Изучить языковые конструкции SystemVerilog для реализации АЛУ (раздел [#инструменты](#Инструменты))

View File

@@ -356,7 +356,7 @@ endmodule
1. Добавьте в `Design Sources` проекта файл [program.mem](program.mem), содержащий программу из Листинга 1.
2. Опишите модуль `CYBERcobra` с таким же именем и портами, как указано в задании (обратите внимание на регистр имени модуля).
1. В первую очередь, необходимо создать счётчик команд и все вспомогательные провода. При создании, **следите за разрядностью**.
2. Затем, необходимо создать экземпляры модулей: памяти инструкции, АЛУ, регистрового файла и сумматора. При подключении сигналов сумматора, надо **обязательно** надо подать нулевое значение на входной бит переноса. Выходной бит переноса подключать не обязательно. Объекту памяти инструкций нужно дать имя `imem`.
2. Затем, необходимо создать экземпляры модулей: памяти инструкции, АЛУ, регистрового файла и сумматора. При подключении сигналов сумматора, надо **обязательно** подать нулевое значение на входной бит переноса. Выходной бит переноса подключать не обязательно. Объекту памяти инструкций нужно дать имя `imem`.
3. После этого, необходимо описать оставшуюся логику:
1. Программного счётчика. Счётчик должен сбрасываться, когда сигнал _rst_i == 1_.
2. Сигнала управления мультиплексором, выбирающим слагаемое для программного счётчика

View File

@@ -10,32 +10,39 @@ See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
*/
module lab_04_tb_CYBERcobra();
logic [31:0] OUT;
logic clk;
logic rst;
logic [15:0] sw_i;
CYBERcobra DUT(
.clk_i(clk),
.rst_i(rstn),
.sw_i (sw_i ),
.rst_i(rst),
.sw_i (sw_i),
.out_o(OUT)
);
wire [31:0] OUT;
reg clk;
reg rstn;
reg [15:0] sw_i;
initial clk <= 0;
always #5 clk = ~clk;
always #5ns clk = ~clk;
initial begin
logic [15:0] count_num;
$display("Test has been started");
rstn = 1'b1;
#10;
rstn = 1'b0;
sw_i = 16'b100001000; //значение, до которого считает счетчик
#10000;
$display("\n The test is over \n See the internal signals of the CYBERcobra on the waveform \n");
rst = 1'b1;
repeat(2)@(posedge clk);
rst = 1'b0;
count_num = $urandom_range(5, 10);
sw_i = count_num;
repeat(count_num * 2 + 15) begin
@(posedge clk);
end
$display("\nThe test is over.");
$display("See the internal signals of the CYBERcobra on the waveform");
$finish;
#5;
$display("You're trying to run simulation that has finished. Aborting simulation.");
@(posedge clk);
$display("You're trying to run simulation that has finished.");
$display("Aborting simulation.");
$fatal();
end

View File

@@ -338,7 +338,7 @@ _Таблица 7. Описание портов дешифратора кома
## Инструменты
**SystemVerilog** – это язык описания аппаратуры. С помощью этого языка человек объясняет либо синтезатору какое он хочет получить устройство, либо симулятору как он хочет это устройство проверить. Синтезатор – это программа, которая создает из логических элементов цифровое устройство по описанию, предоставляемому человеком. Синтезатору внутри **Vivado** нужно объяснить, что от него нужно. Например, чтобы спросить дорогу у испанца, придется делать это на испанском языке, иначе он ничем не сможет помочь. А если вы хорошо знаете испанский, то скорее всего сможете это сделать еще и разными способами. В **SystemVerilog** точно также одно и то же устройство можно описать разным кодом, но результат синтеза будет одним и тем же. Однако, часто два разных кода, одинаковые по смыслу, могут синтезироваться в разную аппаратуру, хотя функционально они будут идентичны, но могут отличаться, например, скоростью работы. Или одни и те же специальные языковые конструкции могут применяться для синтезирования разных цифровых элементов.
**SystemVerilog** – это язык описания аппаратуры. С помощью этого языка человек объясняет либо синтезатору какое он хочет получить устройство, либо симулятору как он хочет это устройство проверить. Синтезатор – это программа, которая создает из логических элементов цифровое устройство по описанию, предоставляемому человеком. Синтезатору внутри **Vivado** нужно объяснить, что от него требуется. Например, чтобы спросить дорогу у испанца, придется делать это на испанском языке, иначе он ничем не сможет помочь. А если вы хорошо знаете испанский, то скорее всего сможете это сделать еще и разными способами. В **SystemVerilog** точно также одно и то же устройство можно описать разным кодом, но результат синтеза будет одним и тем же. В то же время два одинаковых по смыслу кода могут синтезироваться в разную аппаратуру. Будучи функционально идентичными, они могут отличаться, например, скоростью работы. Или одни и те же специальные языковые конструкции могут применяться для синтезирования разных цифровых элементов.
Декодер – комбинационная схема. Это значит, что каждый раз подавая на вход одни и те же значения, вы будете получать на выходе один и тот же результат.
@@ -429,9 +429,9 @@ _Листинг 2. Прототип декодера инструкций._
2. Модуль может быть описан множеством способов: каждый выходной сигнал может быть описан через собственную комбинационную логику в отдельном блоке `case`, однако проще всего будет описать все сигналы через вложенные `case` внутри одного блока `always_comb`.
3. Внутри блока `always_comb` до начала блока `case` можно указать базовые значения для всех выходных сигналов. Это не то же самое, что вариант `default` в блоке `case`. Здесь вы можете описать состояния, которые будут использованы чаще всего, и в этом случае, присваивание сигналу будет выполняться только в том месте, где появится инструкция, требующая значение этого сигнала, отличное от базового.
4. Далее вы можете описать базовый блок `case`, где будет определен тип операции по ее коду.
5. Определив тип операции, вы сможете определить какая конкретно операция по полям `func3` и `func7` (если данный тип имеет такие поля).
6. Не забывайте, что в случае, если на каком-то из этапов (определения типа, или определения конкретной операции) вам приходит непредусмотренное ISA значение какого-либо поля, необходимо выставить сигнал `illegal_instr_o`.
7. В случае некорректной инструкции, вы должны гарантировать, что не произойдет условный/безусловный переход, а во внешнюю память, регистровый файл, а также регистры контроля и статуса ничего не запишется. Не важно, что будет выполняться на АЛУ, не важно какие данные будут выбраны на мультиплексоре источника записи. Важно чтобы не произошел сам факт записи в любое из устройств (подумайте какие значения для каких сигналов необходимо для этого выставить).
5. Определив тип, вы сможете понять, какая именно вам пришла операция по полям `func3` и `func7` (если данный тип имеет такие поля).
6. Не забывайте, что в случае, если на каком-то из этапов (определения типа или определения конкретной операции) вам приходит непредусмотренное ISA значение какого-либо поля, необходимо выставить сигнал `illegal_instr_o`.
7. В случае некорректной инструкции, вы должны гарантировать, что не произойдет условный/безусловный переход, а во внешнюю память, регистровый файл, а также регистры контроля и статуса ничего не запишется. Неважно, что будет выполняться на АЛУ и какие данные будут выбраны на мультиплексоре источника записи. Важно, чтобы не произошел сам факт записи в любое из устройств (подумайте, какие значения для каких сигналов необходимо для этого выставить).
4. Проверьте модуль с помощью верификационного окружения, представленного в файле [`lab_05.tb_decoder.sv`](lab_05.tb_decoder.sv). Вполне возможно, что после первого запуска вы столкнётесь с сообщениями о множестве ошибок. Вам необходимо [исследовать](../../Vivado%20Basics/05.%20Bug%20hunting.md) эти ошибки на временной диаграмме и исправить их в вашем модуле.
1. Перед запуском моделирования убедитесь, что у вас выбран корректный модуль верхнего уровня в `Simulation Sources`.
5. Данная лабораторная работа не предполагает проверки в ПЛИС

View File

@@ -1,6 +1,6 @@
# Лабораторная работа №7 "Тракт данных"
Микроархитектуру можно разделить на две части: тракт данных и устройство управления. По тракту данных перемещаются данные (из памяти инструкций, регистрового файла, АЛУ, памяти данных, мультиплексоров), а устройство управления (в нашем случае — декодер инструкций) получает текущую инструкцию из тракта и в ответ говорит ему как именно её выполнить, то есть управляет тем, как эти данные будут через проходить тракт данных.
Микроархитектуру можно разделить на две части: тракт данных и устройство управления. По тракту данных перемещаются данные (из памяти инструкций, регистрового файла, АЛУ, памяти данных, мультиплексоров), а устройство управления (в нашем случае — декодер инструкций) получает текущую инструкцию из тракта и в ответ говорит ему как именно её выполнить, то есть управляет тем, как эти данные будут проходить через тракт данных.
## Цель
@@ -69,7 +69,7 @@ _Рисунок 1. Микроархитектура ядра процессор
Программный счётчик (`PC`) теперь также изменяется более сложным образом. Поскольку появился ещё один вид безусловного перехода (`jalr`), программный счётчик может не просто увеличиться на значение константы из инструкции, но и получить совершенно новое значение в виде суммы константы и значения из регистрового файла (см. на самый левый мультиплексор _рис. 1_). Обратите внимание, что младший бит этой суммы должен быть обнулен — таково требование спецификации [[1](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), стр. 28].
Поскольку обращение во внешнюю память требует времени, необходимо приостанавливать программный счётчик, чтобы до конца обращения в память не начались исполняться последующие инструкции. Для этого у программного счётчика появился управляющий сигнал `stall_i`. Программный счётчик может меняться только когда этот сигнал равен нулю (иными словами, инверсия этого сигнала является сигналом `enable` для регистра `PC`).
Поскольку обращение во внешнюю память требует времени, необходимо приостанавливать программный счётчик, чтобы до конца обращения в память не начали исполняться последующие инструкции. Для этого у программного счётчика появился управляющий сигнал `stall_i`. Программный счётчик может меняться только когда этот сигнал равен нулю (иными словами, инверсия этого сигнала является сигналом `enable` для регистра `PC`).
### processor_system
@@ -169,7 +169,7 @@ _Листинг 2. Программа из Листинга 1, представ
## Порядок выполнения задания
1. Внимательно ознакомьтесь микроархитектурной реализацией процессорного ядра. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
1. Внимательно ознакомьтесь с микроархитектурной реализацией процессорного ядра. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
2. Замените файл `program.mem` в `Design Sources` проекта новым файлом [program.mem](program.mem), приложенном в данной лабораторной работе. Данный файл содержит программу из _листинга 1_.
3. Опишите модуль процессорного ядра с таким же именем и портами, как указано в задании.
1. Процесс реализации модуля похож на процесс описания модуля CYBERcobra, однако теперь появляется:

View File

@@ -83,7 +83,7 @@ initial begin
$stop();
not_stopped = 0;
end
@(posedge clk_i);
@(negedge clk_i);
if(!core_stall_o) begin
core_req_i = $random;
core_we_i = $random;

View File

@@ -19,6 +19,6 @@ _Рисунок 1. Подключение LSU в процессорную сис
1. Интегрируйте модули `lsu` и `data_mem` в модуль `processor_system`.
1. Обратите внимание, что из модуля `processor_system` необходимо убрать логику сигнала `stall`, т.к. она была перемещена внутрь модуля `lsu`.
2. После интеграции модулей проверьте процессорную систему с помощью [программы](../07.%20Datapath/#Задание) и верификационного окружения из ЛР№7.
1. Как и в случае с проверкой процессора архитектуры CYBERcobra, вам не будет сказано пройден тест или нет. Вам необходимо самостоятельно, такт за тактом, проверить, что процессор правильно выполняет описанные в [_листинге 1_](../07.%20Datapath/#Задание) ЛР№7 инструкции (см. порядок выполнения задания ЛР№4). Для этого необходимо сперва самостоятельно рассчитать, что именно должна сделать данная инструкция, а потом проверить, что процессор сделал именно это.
1. Как и в случае с проверкой процессора архитектуры CYBERcobra, вам не будет сказано, пройден тест или нет. Вам необходимо самостоятельно, такт за тактом, проверить, что процессор правильно выполняет описанные в [_листинге 1_](../07.%20Datapath/#Задание) ЛР№7 инструкции (см. порядок выполнения задания ЛР№4). Для этого необходимо сперва самостоятельно рассчитать, что именно должна сделать данная инструкция, а потом проверить, что процессор сделал именно это.
2. Обратите внимание на то, как теперь исполняются инструкции `sw`, `sh`, `sb`, `lw`, `lh`, `lb`, `lhu`, `lbu`.
3. Данная лабораторная работа не предполагает проверки в ПЛИС.

View File

@@ -11,7 +11,7 @@
1. Описать модуль контроллера прерываний.
2. Описать модуль контроллера регистров статуса и контроля (**CSR**-контроллер).
## Ход выполнения
## Ход работы
1. Изучение теории по прерываниям и исключениям в архитектуре RISC-V, включая работу с регистрами статуса и контроля (**CSR**) и механизмы реализации прерываний.
2. Реализация схемы обработки прерывания для устройства на основе RISC-V
@@ -237,81 +237,74 @@ _Рисунок 5. Структурная схема контроллера пр
```asm
_start:
# Инициализируем начальные значения регистров
00: li x2, 0x00003FF0 # устанавливаем указатель на верхушку стека
04: # данная псевдоинструкция будет разбита на две
# инструкции: lui и addi
00: li x2, 0x00001F0 # устанавливаем указатель на верхушку стека
04: li x3, 0x00000000 # устанавливаем указатель на глобальные данные
08: li x3, 0x00000000 # устанавливаем указатель на глобальные данные
0С: la x5, trap_handler # псевдоинструкция la аналогично li загружает число,
10: # только в случае la — это число является адресом
08: la x5, trap_handler # псевдоинструкция la аналогично li загружает число,
# только в случае la — это число является адресом
# указанного места (адресом обработчика перехвата)
# данная псевдоинструкция будет разбита на две
# инструкции: lui и addi
14: csrw mtvec, x5 # устанавливаем вектор прерывания
0C: csrw mtvec, x5 # устанавливаем вектор прерывания
18: li x5, 0x00001FFC # готовим адрес верхушки стека прерывания
1С: # данная псевдоинструкция будет разбита на две
# инструкции: lui и addi
10: li x5, 0x000000F0 # готовим адрес верхушки стека прерывания
20: csrw mscratch, x5 # загружаем указатель на верхушку стека прерывания
14: csrw mscratch, x5 # загружаем указатель на верхушку стека прерывания
24: li x5, 0x00010000 # подготавливаем маску прерывания единственного
# входа прерываний
18: li x5, -1 # подготавливаем маску, разрешающую все источники
# прерывания
28: csrw mie, x5 # загружаем маску в регистр маски
1C: csrw mie, x5 # загружаем маску в регистр маски
2С: li x5, 1 # начальное значение глобальной переменной
30: sw x5, 0(x3) # загружаем переменную в память
20: li x5, 1 # начальное значение глобальной переменной
24: sw x5, 0(x3) # загружаем переменную в память
34: li x6, 0 # начальное значение, чтобы в симуляции не было xxx
38: li x7, 0 # начальное значение, чтобы в симуляции не было xxx
28: li x6, 0 # начальное значение, чтобы в симуляции не было xxx
2C: li x7, 0 # начальное значение, чтобы в симуляции не было xxx
# Вызов ecall исключительно из хулиганских соображений, поскольку в данной
# микроархитектурной реализации это приведет к появлению illegal_instr и
# последующей обработке исключения
3С: ecall
30: ecall
# Вызов функции main
main:
40: beq x0, x0, main # бесконечный цикл, аналогичный while (1);
34: beq x0, x0, main # бесконечный цикл, аналогичный while (1);
# ОБРАБОТЧИК ПЕРЕХВАТА
# Без стороннего вмешательства процессор никогда не перейдет к инструкциям ниже,
# Без стороннего вмешательства процессор никогда не перейдёт к инструкциям ниже,
# однако в случае перехвата в программный счетчик будет загружен адрес первой
# нижележащей инструкции.
# Сохраняем используемые регистры на стек
trap_handler:
44: csrrw x5, mscratch, x5 # меняем местами mscratch и x5
48: addi x5, x5, -16 # поднимаем верхушку стека на 16 байт вверх
38: csrrw x2, mscratch, x2 # меняем местами mscratch и x2
3C: addi x2, x2, -16 # поднимаем верхушку стека на 16 байт вверх
# (указатель на стек всегда должен быть выровнен
# границе в 16 байт)
4С: sw x6, 0(x5) # сохраняем x6 на стек mscratch
50: sw x7, 4(x5) # сохраняем x7 на стек mscratch
# по границе в 16 байт)
40: sw x6, 0(x2) # сохраняем x6 на стек mscratch
44: sw x7, 4(x2) # сохраняем x7 на стек mscratch
# Проверяем произошло ли прерывание
54: csrr x6, mcause # x6 = mcause
58: li x7, 0x80000010 # загружаем в x7 код того, что произошло прерывание
5С: # данная псевдоинструкция будет разбита на две
48: csrr x6, mcause # x6 = mcause
4C: li x7, 0x80000010 # загружаем в x7 код того, что произошло прерывание
50: # данная псевдоинструкция будет разбита на две
# инструкции: lui и addi
60: bne x6, x7, exc_handler # если коды не совпадают, переходим к проверке
54: bne x6, x7, exc_handler # если коды не совпадают, переходим к проверке
# на исключение
# Обработчик прерывания
64: lw x7, 0(x3) # загружаем переменную из памяти
68: addi x7, x7, 3 # прибавляем к значению 3
6С: sw x7, 0(x3) # возвращаем переменную в память
70: j done # идем возвращать регистры и на выход
58: lw x7, 0(x3) # загружаем переменную из памяти
5C: addi x7, x7, 3 # прибавляем к значению 3
60: sw x7, 0(x3) # возвращаем переменную в память
64: j done # идем возвращать регистры и на выход
exc_handler: # Проверяем произошло ли исключение
74: li x7, 0x0000002 # загружаем в x7 код того, что произошло исключение
78: bne x6, x7, done # если это не оно, то выходим
68: li x7, 0x0000002 # загружаем в x7 код того, что произошло исключение
6C: bne x6, x7, done # если это не оно, то выходим
# Обработчик исключения
7С: csrr x6, mepc # Узнаём значение PC (адреса инструкции,
70: csrr x6, mepc # Узнаём значение PC (адреса инструкции,
# вызвавшей исключение)
80: lw x7, 0x0(x6) # Загружаем эту инструкцию в регистр x7.
74: lw x7, 0x0(x6) # Загружаем эту инструкцию в регистр x7.
# В текущей микроархитектурной реализации это
# невозможно, т.к. память инструкций отделена от
# памяти данных и не участвует в выполнении
@@ -325,19 +318,19 @@ exc_handler: # Проверяем произошло ли иск
# Например, если это операция умножения — вызвать
# подпрограмму умножения.
84: addi x6, x6, 4 # Увеличиваем значение PC на 4, чтобы после
78: addi x6, x6, 4 # Увеличиваем значение PC на 4, чтобы после
# возврата не попасть на инструкцию, вызвавшую
# исключение.
88: csrw mepc, x6 # Записываем обновленное значение PC в регистр mepc
8С: j done # идем восстанавливать регистры со стека и на выход
7C: csrw mepc, x6 # Записываем обновленное значение PC в регистр mepc
80: j done # идем восстанавливать регистры со стека и на выход
# Возвращаем регистры на места и выходим
done:
90: lw x6, 0(x5) # возвращаем x6 со стека
94: lw x7, 4(x5) # возвращаем x7 со стека
98: addi x5, x5, 16 # опускаем верхушку стека обратно на 16 байт вниз
9С: csrrw x5, mscratch, x5 # меняем обратно местами x5 и mscratch
A0: mret # возвращаем управление программе (pc = mepc)
84: lw x6, 0(x2) # возвращаем x6 со стека
88: lw x7, 4(x2) # возвращаем x7 со стека
8C: addi x2, x2, 16 # опускаем верхушку стека обратно на 16 байт вниз
90: csrrw x2, mscratch, x2 # меняем обратно местами x2 и mscratch
94: mret # возвращаем управление программе (pc = mepc)
# что означает возврат в бесконечный цикл
```

View File

@@ -1,13 +1,10 @@
00004137
FF010113
1f000113
00000193
04400293
00028293
03800293
30529073
000022B7
FFC28293
0F000293
34029073
000102B7
FFF00293
30429073
00100293
0051A023
@@ -15,10 +12,10 @@ FFC28293
00000393
00000073
00000063
340292F3
ff028293
0062A023
0072A223
34011173
FF010113
00612023
00712223
34202373
800003B7
01038393
@@ -34,8 +31,8 @@ ff028293
00430313
34131073
0040006F
0002A303
0042A383
01028293
340292F3
00012303
00412383
01010113
34011173
30200073

View File

@@ -82,7 +82,7 @@ irq_o_is_not_0: assert property (
irq_cause_o_mcause: assert property (
@(posedge clk_i) disable iff ( rst_i )
irq_o |-> $onehot0(irq_cause_o[19:4])
)else $error("error value on irq_cause_o: %08h, should be onehot", $sampled(irq_cause_o[20:5]));
)else $error("error value on irq_cause_o: %08h, should be onehot", $sampled(irq_cause_o[19:4]));
irq_cause_o_borders: assert property (
@(posedge clk_i) disable iff ( rst_i )

View File

@@ -16,7 +16,7 @@ module lab_15_tb_system();
logic flash_rx;
logic tx_o;
logic ps2_clk, ps2_dat;
logic sw_i;
logic [15:0] sw_i;
logic tb_rx;
logic flashing_is_done = 0;
logic core_reset;

View File

@@ -17,7 +17,7 @@ OBJCOPY = $(CC_PATH)/$(CC_PREFIX)-objcopy
SIZE = $(CC_PATH)/$(CC_PREFIX)-size
OBJS = core_main.o core_list_join.o core_matrix.o core_state.o core_util.o \
barebones/core_portme.o barebones/startup.o barebones/cvt.o \
startup.o barebones/core_portme.o barebones/cvt.o \
barebones/ee_printf.o

View File

@@ -139,7 +139,7 @@ endmodule
module testbench();
logic A, B, C;
adder DUT(
half_adder DUT(
.A(A), // <- здесь будет ошибка,
// т.к. в модуле half_adder нет порта 'A'
.b(B),