Предполагается, что многопоточный вариант софт-ядра, позволит эффективнее встраивать его в FPGA-проекты в качестве управляющего, контролирующего блока. Выделенные теневые регистры состояний позволят упростить переключение контента между программными задачами и, дополнительно, упростят блок обработки прерываний. Следование концепции архитектуры RISC-V в некоторых моментах упрощает написание и поддержку программного кода на языках высокого уровня хотя в практике относительно небольших, или сильно специфических проектов выгоднее работать в рамках виртуальных языковых машин, или разрабатывать DSL. . В предложенной реализации микроархитектуры контексты потоков сохраняются в наборах т.н. теневых регистров, отображаемых в момент выполнения потока на регистры общего назначения x0-x31 и программный счетчик (PC). Предлагаемая концепция управления потоками выполнения предназначена, прежде всего, для софт-процессоров, и для систем с одним уровнем привилегий – машинным – уровень микроконтроллерных встраиваемых систем. Все потоки предполагаются равноправными и работающими в едином адресном пространстве. Защита данных потоков и контроль за доступом к общим переменным выносится на уровень программного обеспечения. Рассмотрение ведётся для минимального набора инструкций I+Zicsr (целочисленные операции плюс работа с регистрами специального назначения (CSR). Для управления и настройки параметров потоков предлагается задействовать набор CSR-регистров.

Введение

На работу над многопоточным вариантом вдохновили процессоры семейства Xcore компании XMOS, специализирующейся на процессорах/микроконтроллерах для встраиваемых систем. Решения XMOS традиционно включают в себя элементы, которые традиционно требовали бы использования компонентов другого класса [1]. Там, где традиционно можно использовать микроконтроллер для управления конструкцией, DSP для обработки сигналов и, возможно, CPLD для подключения к сложному цифровому интерфейсу, процессоры XMOS могут выполнять эти три задачи в одном устройстве, используя единый программный процесс на основе программного обеспечения. Их характерные черты – аппаратная многопоточность, возможность масштабирования количества ядер/процессоров в системе, гибкие программно-конфигурируемые порты ввода-вывода. В ранних версиях своих процессоров XMOS применяли свои RISC ядра, были серии с комбинацией RISC-ядер и ARM-ядра. В 2023м году компания анонсировала вариант процессора с ядрами RISC-V (рис.1).

Рис. 1. Двухядерный процессор XMOS с восьмипоточными RISC-V ядрами [2].
Рис. 1. Двухядерный процессор XMOS с восьмипоточными RISC-V ядрами [2].

Вторым побуждающим мотивом экспериментов  является желание в решениях софт-ядер для FPGA уйти от необходимости введения в состав софт-процессора контроллера прерываний.
Разработка многопоточной микроархитетуры софт-процессора актуальна для систем на базе FPGA по следующим причинам:
- упрощение обработки множества асинхронных событий за счет нескольких потоков без необходимости программного переключения контекста;
-упрощение контроллера прерываний;
- упрощение программного кода и уменьшение его размера.

Одной из задач работы была задача сохранить совместимость многопоточной микроархитектуры с архитектурой RISC-V.
В спецификации архитектуры RISC-V заявлена поддержка многопоточности в виде т.н. аппаратно-поддерживаемых потоков — хартов (hart). Собственно, это и было реализовано в рассматриваемой микроархитектуре. Для каждого из потоков(хартов) предусмотрено хранение состояния потока в массиве теневых регистров (основные регистры x0-x31 и счетчик программ)  — каждый набор регистров хранит архитектурное состояние потока.
Общими ресурсами являются исполнительные логические блоки (периферийные устройства, АЛУ), память (программ и данных). Также общими для всех потоков является блок регистров специального назначения (CSR) — регистры состояния, таймеры, счетчики производительности и тп...

"Эволюция" из многотактного поэтапного конвейера

Попробуем несколько модифицировать многотактный вариант процессора с мелким разбитием этапов выполнения команд, добавив ему поддержку многопоточности в виде «теневых» копий основных архитектурных регистров. В терминологии RISC-V аппаратно-поддерживаемый поток называется хартом (hart) [2-4]. Общая структура ядра напоминает цепочку связанных сегментов – рис.2

Рис. 2. Базовая структура ядра.
Рис. 2. Базовая структура ядра.

Ключевая идея – для каждого из хартов хранение состояния потока(нити) в массиве теневых регистров. При этом каждая из копий регистров хранит архитектурное состояние нити. Общими ресурсами остаются логические блоки, память. Также имеет смысл сохранить общим для всех хартов блок регистров специальных функций (как правило, это системные таймеры, счетчики производительности и тп).

Блок CSR-регистров в таком варианте должен будет иметь два раздельных адресных входа – на чтение его данных и на запись данных в него (из-за того, что он оставлен общим для всех хартов и операции чтения/записи осуществляются на разных этапах):

module rv_csr
#(
  parameter DATA_WIDTH=32, 
  parameter ADDR_WIDTH=12,
  parameter CSR_size = 32
  )
( input clk,
  input [(ADDR_WIDTH-1):0] csr_addr_in,
  input [(DATA_WIDTH-1):0] csr_in,
  input [(ADDR_WIDTH-1):0] csr_addr_out,
  output reg [(DATA_WIDTH-1):0] csr_out,
  input csr_wr,
  input en
);
// csr register file
reg [ADDR_WIDTH-1:0] csr_reg[0:CSR_size-1];

always @ (posedge clk)
begin
  case (csr_addr_in)
    32'h0 : begin
      if (csr_wr&en) begin
        csr_reg[0] <= csr_in;
      end 
    end
    default: begin
      csr_reg[1] <= 32'h555;
    end
  endcase
  case (csr_addr_out)
    32'h0 : begin
      csr_out <= csr_reg[0];
    end
    default: begin
      csr_out<=32'hAAA;
    end
  endcase
end

endmodule

 Аналогично файл-регистр также должен иметь возможность записывать и считывать данные регистров, соотнесенных различным хартам – фактически определяем массив или, если угодно, стек файл-регистров. (для отладочных целей и бОльшей наглядности в коде далее использован вариант файл-регистра с дополнительными выходами для отслеживания состояния отдельных регистров).

module rv_reg_file
#(
  parameter DATA_WIDTH=32, 
  parameter ADDR_WIDTH=5
  )
( input clk,
  input [(ADDR_WIDTH-1):0] rs1,
  input [(ADDR_WIDTH-1):0] rs2,
  input [(ADDR_WIDTH-1):0] rd,
  output reg [(DATA_WIDTH-1):0] Rs1_out,
  output reg [(DATA_WIDTH-1):0] Rs2_out,
  input [(DATA_WIDTH-1):0] Rd_input,
  input [2:0] hart_in, // hard to read out data from
  input [2:0] hart_out, // hard to write data to
  input we,
  input en,
  output reg [(DATA_WIDTH-1):0] x1_out,
  output reg [(DATA_WIDTH-1):0] x2_out,
  output reg [(DATA_WIDTH-1):0] x3_out,
  output reg [(DATA_WIDTH-1):0] x4_out,
  output reg [(DATA_WIDTH-1):0] x5_out
);

// RAM array
  reg [DATA_WIDTH-1:0] ram[0:2**ADDR_WIDTH-1][0:7];
  wire rd_nonzero;
  wire rs1_nonzero;
  wire rs2_nonzero;
  assign rd_nonzero = |rd;
  assign rs1_nonzero = |rs1;
  assign rs2_nonzero = |rs2;

always @ (posedge clk)
  begin
    if (en & we & rd_nonzero) ram[rd][hart_in] <= Rd_input;
  end

always @ (*)
  begin
    Rs1_out <= rs1_nonzero ? ram[rs1][hart_out] : 32'h0;
    Rs2_out <= rs2_nonzero ? ram[rs2][hart_out] : 32'h0;
    x1_out <= ram[1][hart_out];
    x2_out <= ram[2][hart_out];
    x3_out <= ram[3][hart_out];
    x4_out <= ram[4][hart_out];
    x5_out <= ram[5][hart_out];
  end
// */
endmodule

Программный счетчик также будет массивом регистров.

module rv_pc
#(
parameter WIDTH=32
)
(
  input clk,
  input rst_n,
  input en,
  input pc_load,
  input [WIDTH-1:0] pc_next,
  output [WIDTH-1:0] pc,
  output reg [WIDTH-1:0] pc_plus,
  input [2:0] hart_in,
  input [2:0] hart_out
);
reg [WIDTH-1:0] pc_reg[0:7];
// read initial PCs data values
initial
$readmemh("pc.txt",pc_reg);

always@(posedge clk or negedge rst_n)
  begin
    if (en) begin
      if(pc_load==1'b1)
        pc_reg[hart_in] <= pc_next;
      else
        pc_reg[hart_in] <= pc_reg[hart_in] + 3'h4;
      end
    end
assign pc = pc_reg[hart_out];

always @ *
  begin
    pc_plus <= pc_reg[hart_out] + 3'h4;
  end

endmodule

В файле «pc.txt» - начальные значения регистров.

Как и ожидалось, «ленивый» вариант преобразования многотактного процессора в многопоточный простой заменой файл-регистра и счетчика не работоспособен, требуются некоторый механизм управления.

Для управления выборкой данных хартов введём группу регистров – hart-reg, объединенных в линейку. Выходные сигналы – номер харта и бит активности харта. При сбросе регистры инициируются нулями и биты активности хартов сброшены.

На линейку в процессе функционирования должна подаваться  последовательность хартов с кодами активности. Сама последовательность может, например, хранится в отдельной небольшой памяти – назовем ее hart_table. Hart_table адресуется циклическим счетчиком.

Для текущего примера создадим простенький «системный таймер» разрядностью 64-бита (на самом деле можно любой приемлемой разрядности, в данном случае 64 бита выбрано по аналогии с традиционными таймерами-счетчиками в реализациях RISC-V архитектуры). Младшие разряды счетчика будут адресовать память таблицы хартов. Сам счетчик потом можно будет использовать в качестве счетчика тактов работы с момента старта системы, например.

Биты активности харта также должны будут использованы для управления записью в такие модули процессорного ядра, как программный счетчик, файл-регистр, запись в память (если харт не активен – запись не будет произведена). Также инверсный сигнал активности харта подается на вход «сброса конвейера» АЛУ (да, для данной реализации будет взята версия АЛУ конвейеризированного процессорного ядра).

Уровни «разделяются» промежуточными регистрами (обычные регистры хранения), которые фиксируют данные (регистры R0 – R6). Ряд сигналов просто «пробрасываются» по этапам (напрямую между регистрами) в том случае, если они не задействованы в них, но требуются на последующих – всё как и было в простом многотактном варианте.

Рис. 3. Структура многопоточного процессора
Рис. 3. Структура многопоточного процессора

Итоговые микроархитектурные блоки многопоточного варианта процессора:
rv_pc – программный счетчик (массив из нескольких регистров);
rv_mem – блок памяти (программная и оперативная;
rv_desh – дешифратор команд (слова-инструкции) с входом «сброса конвейера»;
rv_imm – формирователь непосредственного значения из слова-инструкции;
rv_reg_file – файл-регистр (массив из нескольких файл-регистров);
rv_ops_mux – коммутатор операндов для АЛУ;
rv_cmp – формирователь сигнала разрешения перехода;
rv_alu_v – АЛУ;
rv_rez_mux – коммутатор результатов;
rv_csr – блок регистров специального назначения;
rv_r_reg – регистры хранения результатов промежуточных этапов;
rv_hart_reg – регистры хранения номеров и состояния активности хартов;
rv_timer – системный таймер;
rv_hart_table – память хранения списка и состояния хартов.

Описание памяти таблицы хартов – в текущем варианте – память на 8 записей, выходная разрядность 4 бита, старший бит – бит активности харта, младшие три – номер харта:

module rv_hart_table
#(
  parameter DATA_WIDTH=4, 
  parameter ADDR_WIDTH=3
  )
( input clk,
  input [(ADDR_WIDTH-1):0] h_addr,
  output reg [(DATA_WIDTH-1):0] h_out,
  input we,
  input en
);
// ROM array
reg [DATA_WIDTH-1:0] rom [0:2**ADDR_WIDTH-1] ;
// read ROM content from file
initial
$readmemh("hart_table.txt",rom);
always @ (*)
  begin
    h_out <= rom[h_addr];
  end
endmodule

//Системный таймер:

module rv_timer
#(
  parameter DATA_WIDTH=64
  )
( input clk,
  input rst,
  output [(DATA_WIDTH-1):0] timer
);

reg [(DATA_WIDTH-1):0] cnt;

always@(posedge clk or negedge rst)
  begin
    if(!rst)
      cnt <= 64'h0;
    else
      cnt <= cnt + 1'b1;
  end

assign timer = cnt;

endmodule
Рис.4. Соединение таблицы хартов со счетчиком
Рис.4. Соединение таблицы хартов со счетчиком

Соединение линейки регистров для хранения хартов:

wire [3:0] hart1_out;
rv_hart_reg H1(
  .clk(clk),
  .rst(rst),
  .en(1'b1),
  .h_in(hart0_out),
  .h_out(hart1_out)
);

wire [3:0] hart2_out;

rv_hart_reg H2(
  .clk(clk),
  .rst(rst),
  .en(1'b1),
  .h_in(hart1_out),
  .h_out(hart2_out)
);

wire [3:0] hart3_out;

rv_hart_reg H3(
  .clk(clk),
  .rst(rst),
  .en(1'b1),
  .h_in(hart2_out),
  .h_out(hart3_out)
);

wire [3:0] hart4_out;

rv_hart_reg H4(
  .clk(clk),
  .rst(rst),
  .en(1'b1),
  .h_in(hart3_out),
  .h_out(hart4_out)
);

wire [3:0] hart5_out;

rv_hart_reg H5(
  .clk(clk),
  .rst(rst),
  .en(1'b1),
  .h_in(hart4_out),
  .h_out(hart5_out)
);

wire [3:0] hart6_out;

rv_hart_reg H6(
  .clk(clk),
  .rst(rst),
  .en(1'b1),
  .h_in(hart5_out),
  .h_out(hart6_out)
);

Тестирование многопоточного ядра

Небольшая тестовая программа - в программе заданы несколько коротких бесконечных цикла, работающих с регистрами х1, х2. Регистр х1 инициируется определенным значением, регистр х2 будет использован, как регистр связи. При простом линейном исполнении программа зациклится на метке l1

Address     Code        Basic                        Line Source
0x00400000  0x000010b7  lui x1,1                     1    lui x1,0x1
0x00400004  0x00c0d093  srli x1,x1,12                2    srli x1,x1,12
0x00400008  0x00108093  addi x1,x1,1                 4    addi x1, x1, 1
0x0040000c  0xffdff16f  jal x2,0xfffffffc            5    jal x2,l1
0x00400010  0x000990b7  lui x1,0x00000099            6    lui x1,0x99
0x00400014  0x000020b7  lui x1,2                     8    lui x1,0x2
0x00400018  0x00c0d093  srli x1,x1,12                9    srli x1,x1,12
0x0040001c  0x00208093  addi x1,x1,2                 11   addi x1, x1, 2
0x00400020  0xffdff16f  jal x2,0xfffffffc            12   jal x2,l2
0x00400024  0x000990b7  lui x1,0x00000099            13   lui x1,0x99
0x00400028  0x000030b7  lui x1,3                     15   lui x1,0x3
0x0040002c  0x00c0d093  srli x1,x1,12                16   srli x1,x1,12
0x00400030  0x00308093  addi x1,x1,3                 18   addi x1, x1, 3
0x00400034  0xffdff16f  jal x2,0xfffffffc            19   jal x2,l3
0x00400038  0x000990b7  lui x1,0x00000099            20   lui x1,0x99
0x0040003c  0x000040b7  lui x1,4                     22   lui x1,0x4
0x00400040  0x00c0d093  srli x1,x1,12                23   srli x1,x1,12
0x00400044  0x00408093  addi x1,x1,4                 25   addi x1, x1, 4
0x00400048  0xffdff16f  jal x2,0xfffffffc            26   jal x2,l4
0x0040004c  0x000990b7  lui x1,0x00000099            27   lui x1,0x99
0x00400050  0x000050b7  lui x1,5                     29   lui x1,0x5
0x00400054  0x00c0d093  srli x1,x1,12                30   srli x1,x1,12
0x00400058  0x00508093  addi x1,x1,5                 32   addi x1, x1, 5
0x0040005c  0xffdff16f  jal x2,0xfffffffc            33   jal x2,l5
0x00400060  0x000990b7  lui x1,0x00000099            34   lui x1,0x99
0x00400064  0x000060b7  lui x1,6                     36   lui x1,0x6
0x00400068  0x00c0d093  srli x1,x1,12                37   srli x1,x1,12
0x0040006c  0x00608093  addi x1,x1,6                 39   addi x1, x1, 6
0x00400070  0xffdff16f  jal x2,0xfffffffc            40   jal x2,l6
0x00400074  0x000990b7  lui x1,0x00000099            41   lui x1,0x99
0x00400078  0x000070b7  lui x1,7                     43   lui x1,0x7
0x0040007c  0x00c0d093  srli x1,x1,12                44   srli x1,x1,12
0x00400080  0x00708093  addi x1,x1,7                 46   addi x1, x1, 7
0x00400084  0xffdff16f  jal x2,0xfffffffc            47   jal x2,l7
0x00400088  0x000990b7  lui x1,0x00000099            48   lui x1,0x99
0x0040008c  0x000080b7  lui x1,8                     50   lui x1,0x8
0x00400090  0x00c0d093  srli x1,x1,12                51   srli x1,x1,12
0x00400094  0x00808093  addi x1,x1,8                 53   addi x1, x1, 8
0x00400098  0xffdff16f  jal x2,0xfffffffc            54   jal x2,l8
0x0040009c  0x000990b7  lui x1,0x00000099            55   lui x1,0x99

Для исполнения на многопоточном процессоре массив программных счетчиков инициируется значениями адресов начала каждого из циклов – в данном примере – адреса:

0x0, 0x14, 0x28, 0x3С, 0x50, 0x64, 0x78, 0x8C.

Таблица хартов – перечисление номеров хартов с 0 по 7 в 4-битном представлении. Например, если активен должен быть харт с номером 0, то в таблице он будет представлен цифрой 8 (4b1000), если с номером 3, то 0xB (4b1011) и так далее.

PC.txt

Hart_table.txt

@00

0
14
28
3c
50
64
78
8c

@00

8
9
2
3
4
5
6
7

Рис.5 . Временные диаграммы работы многопоточного процессора при двух активных хартах (потоках).
Рис.5 . Временные диаграммы работы многопоточного процессора при двух активных хартах (потоках).

На диаграмме представлены значения:

  • сигналы сброса и тактовый;

  • номера хартов (с битом активности);

  • значение программного счетчика и выбранное из памяти слово инструкции;

  • для АЛУ - опкод команды, операнды и результат;

  • для файл-регистра текущие выходы и вход с разрешением записи;

  • тестовые выходы первых 4 регистров файл-регистра с указанием, к какому харту они относятся.

Для регистра Х1 0-го активного харта видна сначала его загрузка значением 0х1000, потом сдвиг его до 0х1, аналогично для 1-го активного харта видна его загрузка значением 0х2000, потом сдвиг его до 0х2.

Предложенный вариант многопоточной микроархитектуры RISC-V позволит осуществлять совместную работу нескольких независимых, или созависимых программ в рамках одного аппаратного ядра. Все программные потоки в предложенном варианте работают с общей памятью, поскольку в аппаратной части не предусмотрено мер по защите данных от одновременной модификации-записи, забота об их сохранности и корректном доступе ложится на программиста. Микроархитектурные блоки системного таймера и таблицы хартов для соответствия архитектуре RISC-V  необходимо ввести в состав блоков регистров специального назначения (CSR) – появится возможность их программной модификации штатными средствами, и будет формальное соответствие канонической архитектуре.

Дальнейшее развитие микроархитектуры будет заключаться в развитии механизмов управвления хартами, как событийными (аналог прерываний), так и программными – остановка/запуск харта самим собой или другими хартами.

Для управления наборами теневых регисров вводится группа регистров – hart-reg, объединенных в линейку. Выходные сигналы этих регистров – номер харта и бит активности харта. При сбросе регистры инициируются нулями и биты активности хартов сброшены.

На линейку регистров  hart-reg в процессе функционирования  подается  последовательность хартов с кодами активности. Сама последовательность потоков/хранится в отдельной небольшой памяти –hart_table. Hart_table адресуется циклическим счетчиком.

Роль циклического счетчика играют младшие разряды системного таймера.

Биты активности харта также  используются для управления записью в такие модули процессорного ядра, как программный счетчик, файл-регистр, запись в память (если харт не активен – запись не будет произведена).

Перечень потоков хранится в модуле rv_hart_table – память хранения списка и состояния хартов.

Управление аппаратными потоками в архитектуре RISC-V

По спецификации архитектуры RISC-V и данным открытых источников, управление аппаратными потоками выносится за рамки архитектуры. В большинстве реализаций управление потоками выносится на уровень прикладного или системного программного обеспечения (в зависимости от типа и назначения процессора).

Специфицированы только несколько регистров специального назначения (регистры, приведенные ниже соответствуют машинному уровню доступа, для систем с различным уровнем привилегий эти регистры также отображаются на адреса доступные непривилегированному коду):

mhartid (только чтение)– Hart ID Register – идентификатор аппаратного потока, который выполняет текущий код (для однопоточных процессоров mhartid = 0);

mstatus (чтение/запись) – Machine Status Register – регистр содержит текущее состояние и управляет текущим состоянием аппаратного потока.

Однако, ни в одном из этих регистров механизмы прямого управление состоянием потока (выполняется/не выполняется) не предусмотрены, по крайней мере для ядер микроконтроллерного класса. Способы взаимодействия потоком между собой также будут зависеть от конкретной реализации, и, как правило, взаимодействия потоков регулируются средствами операционных систем.

Возможные способы взаимодействия потоков в системах с одним аппаратным уровнем привилегий:

  • через общую память (часто для исключения гонок по данным/переменным, вероятно, лучше задействовать команды А-расширения системы команд, при его поддержке), иначе контроль выносится на программный уровень;

  • через прерывания/исключения;

  • через регистры специального назначения (CSR-регистры) – подобный вариант не встречался, но, формально, не запрещен в микроархитектурных реализациях.

Варианты перехода потока между активным и пассивным состояниями.

Программные (синхронные) события:

  • самостоятельно (поток имеет возможность перевести самого себя в состояние “не активен” – например, изменяя переменную, бит в выделенном CSR-регистре);

  • по команде другого потока (аналогично, или через общую переменную, или через бит CSR-регистра);

Асинхронные события — реакция на:

  • по событию периферийных устройств – таймеры, счётчики, приемопередающие устройства (UART, SPI, I2C, I2S и тп.);

  • по событиям обмена данными между потоками (чтение/запись).

Фактически, задача сводится к созданию некоторого подобия контроллера прерываний, которая упрощается тем, что можно следовать соглашениям, что за обработку тех или иных событий будет отвечать строго определенный аппаратный поток.

Помимо признака активности, поток характеризуется значением программного счетчика – т.е. тем кодом, который поток призван исполнять. Значениями программных счетчиков потоков также можно  управлять – через адресное пространство памяти, или через CSR-регистры.

Стратегически правильнее будет вынести функции управления потоками на уровень CSR-регистров – в вариантах архитектуры с поддержкой привилегированных команд доступ к ним чаще всего ограничивается средствами операционных систем/системных мониторов.

Для максимального исключения конфликтов с существующими реализациями и расширениями системы команд в качестве базовых адресов CSR-регистров “дальние адреса” – например, 0x3800 и далее.

Регистры управления hart-ами

Адрес

Обозначение

Описание

0x3800 - …

Hart_table

Таблица потоков, находящихся в “жесткой ротации” – те наборы теневых регистров, выполение которых разрешено или планируется в ходе работы – задаёт порядок выполнения потоков.

Текущий поток отображается также в регистре текущего потока (hartid) (младшая часть), бит активности потока отображается в регистре состояния потока (hart_state)

0x3820 - …

PC_table

Таблица значений программных счетчиков потоков в «жесткой ротации»

0х3840 - ...

Sensivity_list

Набор битовых полей, показывающих как/посредством чего поток может изменять свой бит активности

Биты слов в полях Sensiviti_list кодируют возможные для потока условия изменения его состояния:

  • поток управляет своим состоянием сам;

  • поток чувствителен к состоянию и сигналам системных и периферийных устройств - таймеры, интерфейсы;

  • поток реагирует на уровни линий ввода-вывода.

Заключение

Все потоки предполагаются равноправными и работающими в едином адресном пространстве. Защита данных потоков и контроль за доступом к общим переменным потоков выносится на уровень программного обеспечения. Предлагаемый вариант управления потоками требует лишь минимального набора инструкций I+Zicsr (целочисленные операции плюс работа с регистрами специального назначения (CSR), и не конфликтует с устоявшимися стандартами и расширениями архитектуры RISC-V.

https://riscv-alliance.ru/material/risc-v-dlya-fpga-arhitektura-mikroarhitekturnye-realizaczii/

1.  Multicore microcontrollers of the XCORE family from XMOS [Электронный ресурс].– Режим доступа: https://cyberleninka.ru/article/n/multiyadernye-mikrokontrollery-semeystva-xcore-ot-xmos

2.       Using RISC-V to define SoCs in software _ XMOS [Электронный ресурс].– Режим доступа: https://www.xmos.com/using-risc-v-to-define-socs-in-software/

3.       The RISC-V Instruction Set Manual. Volume I: User-Level ISA Document Version 2.2. Editors: Andrew Waterman1, Krste Asanov

4.       Сomputer architecture - What is a hardware thread in RISC-V_ - Electrical Engineering Stack Exchange [Электронный ресурс].– Режим доступа: https://electronics.stackexchange.com/questions/580645/what-is-a-hardware-thread-in-risc-v

5.       Trusted Hart for Mobile RISC-V Security [Электронный ресурс].– Режим доступа: https://www.computer.org/csdl/proceedings-article/trustcom/2022/942500b587/1LFM9tik1IQ

6.       RISC-V Assembly Language Programmer Manual Part I developed by: SHAKTI Development Team @ iitm ’20 shakti.org.inRISCV Green Card_v. [Электронный ресурс].– Режим доступа: https://www.cl.cam.ac.uk/teaching/1617/ECAD+Arch/files/docs/RISCVGreenCardv8-20151013.pdf

7.       Stephen Smith. Programming in the RISC-V assembly language / translated from English by A. V. Logunov; edited by A. Yu. Romanov. – M.: DMK Press – 276 p

8.       Adding custom instructions compilation support, to RISCV toolchain. [Электронный ресурс].– Режим доступа:  https://medium.com/@viveksgt/adding-custom-instructions-compilation-support-to-riscv-toolchain-78ce1b6efcf4

9.        Создание RISC-V ядер [Электронный ресурс].– Режим доступа: https://riscv-alliance.ru/material/sozdanie-risc-v-yader/

10.     RISC-V для FPGA – архитектура, микроархитектурные реализации  [Электронный ресурс].– Режим доступа: https://riscv-alliance.ru/material/risc-v-dlya-fpga-arhitektura-mikroarhitekturnye-realizaczii/