32-битный персональный компьютер nedoPC-5 на RISC-V

Публичный форум для http://www.nedopc.org/nedopc

Moderator: Shaos

User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

32-битный персональный компьютер nedoPC-5 на RISC-V

Post by Shaos »

Недо-вики: http://www.nedopc.org/nedopc/5

Репозиторий с исходниками:
https://gitlab.com/nedopc/npc5

Хакадей прожэкт:
https://hackaday.io/project/162397-nedopc-5

UPDATE 22-NOV-2018: Первую версию компьютера (альфа) nedoPC-5-A планирую сделать вот на этой платке, что чуть ниже, которая имеет на борту FPGA iCE40 5K (с 5 тыщами лутов и 1 мегабитом блочной памяти), и назвать сей продукт "IceAge" :mrgreen:
Для FPGA надо будет написать на "Вырвиглотке" свою реализацию RISC-V (как минимум подсистемы RV32I) и назвать её скажем Retro-V, т.к. она будет давать людям возможность работать с этой платкой, как старым добрым микропроцессором - у платки будет внешняя шина адреса (16 бит) и данных (8 бит) с некоторым количеством управляющих сигналов (внутренняя память тоже возможно будет задействована, но там будет скажем подпрограммы для имитации команд работы с плавающей точкой RV32F и RV32D, которые будут вызываться по исключительному состоянию нелегального опкода ну и например графическая подсистема : )
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

Получил бесплатно платку UPDuino v2.0 для экспериментов ;)
UPDuino2.jpg
A low cost, minimalistic Arduino-like breakout board with Lattice Ultraplus FPGA and a USB programming device

Gnarly Grey UPDuino v2.0 Board Features:
  • Lattice UltraPlus FPGA
  • 5.3K LUTs, 1Mb SPRAM, 120Kb DPRAM, 8 Multipliers
  • FTDI FT232H USB to SPI Device for FPGA programming
  • 12Mhz Crystal Oscillator Clock Source
  • 34 GPIO on 0.1” headers
  • SPI Flash, RGB LED, 3.3V and 1.2V Regulators
  • Open source PCB design in Eagle and Programming Instructions
P.S. Она опенсорц даже: https://github.com/gtjennings1/UPDuino_v2_0

P.P.S. 9 декабря 2018 исправил ошибку в картинке ниже - ногу 36 они неправильно назвали 35 т.е. на картинке как бы две ноги с именем 35 было - я полдня убил пока понял почему у меня не то этот выход показывает...
You do not have the required permissions to view the files attached to this post.
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

Схема
You do not have the required permissions to view the files attached to this post.
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

Пытаюсь поставить среду разработки от Lattice Semiconductor - iCEcube2: http://www.latticesemi.com/en/Products/DesignSoftwareAndIP/FPGAandLDS/iCEcube2
Пришлось восстанавливать старый аккаунт у них (заводил в 2005 году) - без него даже на скачку не пускало, потом запросил лицензионный ключ, который привязан к MAC-адресу
Скачанный установщик под линух это 32-битный бинарь размером 396 мегов, который требует кучу 32-битных либ (у меня 64-битная система, так что приходится по одной либе ставить)
Как закончу установку напишу сюда какие либы пришлось поставить...

P.S. Запустилось с 11ой попытки (после каждой неудачной попытки ставил вручную либу, на отсутствие которой ругался инсталлятор):

Code: Select all

Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libpng12.so.0: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libSM.so.6: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libXi.so.6: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libXrender.so.1: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libXrandr.so.2: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libXfixes.so.3: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libXcursor.so.1: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libXinerama.so.1: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libfreetype.so.6: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libfontconfig.so.1: cannot open shared object file: No such file or directory
Downloads/iCEcube2setup_Sep_12_2017_1708: error while loading shared libraries: libgthread-2.0.so.0: cannot open shared object file: No such file or directory
Ставил в дебиан следующее: libsm6:i386 libxi6:i386 libxrender1:i386 libxrandr2:i386 libxfixes3:i386 libxcursor1:i386 libxinerama1:i386 libfreetype6:i386 libfontconfig1:i386 libglib2.0-0:i386

Плюс libpng12-0_1.2.50-2+deb8u3_i386.deb качнул с инета (в дебиане уже 16)

P.P.S. Лицензию не принимает - пишет неправильный "hostid" :(
You do not have the required permissions to view the files attached to this post.
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

Всё понятно - эти чудаки смотрят интерфейс eth0, которого у меня нет...

https://j-marjanovic.io/my-first-encounter-with-lattice-semiconductor.html

Описанный на этой страничке трюк с dummy интерфейсом eth0 помог :mrgreen:

P.S. Потом пришлось в баш-скриптах поменять #!/bin/sh на #!/bin/bash т.к. отказывалось работать...
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

Родную программку-программатор ставить не стал, т.к. версия под линух 300 мегов весит и идёт в виде RPM (под ред-хат однако - в дебиане проблематично их ставить)

Вместо этого взял iceprog из состава пакета программ https://github.com/cliffordwolf/icestorm - прошивает битстрим на ура :)
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

Припаял гребёнки контактов - теперь можно на бредборде экспериментировать :)
You do not have the required permissions to view the files attached to this post.
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

Выяснилось, что на схеме UPDuino цвета RGB-светодиода нарисованы неправильно и ноги названы тоже неправильно - должно быть так:

pin 39 - RGB0 - это GRN
pin 40 - RGB1 - это BLU
pin 41 - RGB2 - это RED

В окошке настройки подключения пинов оно автоматически выбралось как надо (после того как я в тексте программы поправил в соответствии с тем, как есть на самом деле):
You do not have the required permissions to view the files attached to this post.
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: RISC V

Post by Shaos »

Строю модель RISC-V с нуля и проверяю в вериляторе - очень занимательно получается :)

Только у меня не настоящий риск, а типа как в микроконтроллерах PIC - только две ступени конвейера и каждая разделена на 4 части т.е. как бы одна команда за 4 такта выполняется (с перехлёстом на время вычитки следующей команды) - тут оно как бы естественно, так как у меня 8-битная шина данных и каждую 32-битную инструкцию можно прочитать только в 4 захода и я по ходу дела ещё и декодирую её по частям :oidea:
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: RISC V

Post by Shaos »

Сделал полное декодирование инструкций и кое-какое исполнение - уже съелось 700+ лутов из 5 тыщ :(
(и это притом, что регистры удалось затолкать внуть блочной памяти)
Зато скорость тактирования предсказывается порядка 40 МГц, что значит 10 миллионов операций в секунду...
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: RISC V

Post by Shaos »

2000 лутов и оно уже печатает "Hello RISC-V!" (правда в вериляторе) :kruto:

Code: Select all

./retro1-mk
make: Entering directory '/data/DOWNLOAD/obj_dir'
g++  -I.  -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -Wno-char-subscripts -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable       -c -o retro1.o ../retro1.cpp
g++  -I.  -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -Wno-char-subscripts -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable       -c -o verilated.o /usr/share/verilator/include/verilated.cpp
g++  -I.  -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -Wno-char-subscripts -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable       -c -o verilated_dpi.o /usr/share/verilator/include/verilated_dpi.cpp
/usr/bin/perl /usr/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include Vretro.cpp Vretro_retro.cpp > Vretro__ALLcls.cpp
/usr/bin/perl /usr/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include Vretro__Dpi.cpp Vretro__Syms.cpp > Vretro__ALLsup.cpp
g++  -I.  -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -Wno-char-subscripts -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable       -c -o Vretro__ALLcls.o Vretro__ALLcls.cpp
g++  -I.  -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -Wno-char-subscripts -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable       -c -o Vretro__ALLsup.o Vretro__ALLsup.cpp
      Archiving Vretro__ALL.a ...
ar r Vretro__ALL.a Vretro__ALLcls.o Vretro__ALLsup.o
ar: creating Vretro__ALL.a
ranlib Vretro__ALL.a
g++    retro1.o verilated.o verilated_dpi.o Vretro__ALL.a    -o Vretro -lm -lstdc++  2>&1 | c++filt
make: Leaving directory '/data/DOWNLOAD/obj_dir'
Hello RISC-V!
Hello RISC-V!
Hello RISC-V!
Hello RISC-V!
Hello RISC-V!
Hello RISC-V!
Hello RISC-V!
....
Статистика по лутам после синтеза в iCEcube2:

Code: Select all

Final Design Statistics
    Number of LUTs      	:	1994
    Number of DFFs      	:	324
    Number of DFFs packed to IO	:	0
    Number of Carrys    	:	275
    Number of RAMs      	:	4
    Number of ROMs      	:	0
    Number of IOs       	:	35
    Number of GBIOs     	:	1
    Number of GBs       	:	4
    Number of WarmBoot  	:	0
    Number of PLLs      	:	0
    Number of I2Cs      	:	0
    Number of SPIs      	:	0
    Number of DSPs     	:	0
    Number of SBIOODs     	:	0
    Number of LEDDAIPs     	:	0
    Number of RGBADRVs     	:	0
    Number of LFOSCs     	:	0
    Number of HFOSCs     	:	0
    Number of FILTER_50NSs     	:	0
    Number of SPRAMs     	:	0
Device Utilization Summary
    LogicCells                  :	2071/5280
    PLBs                        :	309/660
    BRAMs                       :	4/30
    IOs and GBIOs               :	36/36
    PLLs                        :	0/1
    I2Cs                        :	0/2
    SPIs                        :	0/2
    DSPs                        :	0/8
    SBIOODs                     :	0/3
    RGBADRVs                    :	0/1
    LEDDAIPs                    :	0/1
    LFOSCs                      :	0/1
    HFOSCs                      :	0/1
    SPRAMs                      :	0/4
    FILTER50NSs                 :	0/2
По быстродействию предсказания такие:

Code: Select all

Performance Summary
*******************
Worst slack in design: -3.568
                   Requested     Estimated     Requested     Estimated                Clock        Clock                
Starting Clock     Frequency     Frequency     Period        Period        Slack      Type         Group                
------------------------------------------------------------------------------------------------------------------------
retro|clk          49.5 MHz      41.9 MHz      20.217        23.859        -3.568     inferred     Autoconstr_clkgroup_0
========================================================================================================================
Clock Relationships
*******************
Clocks                |    rise  to  rise    |    fall  to  fall   |    rise  to  fall   |    fall  to  rise  
--------------------------------------------------------------------------------------------------------------
Starting   Ending     |  constraint  slack   |  constraint  slack  |  constraint  slack  |  constraint  slack 
--------------------------------------------------------------------------------------------------------------
retro|clk  retro|clk  |  20.217      -3.568  |  20.217      7.859  |  10.109      1.573  |  10.109      -1.821
==============================================================================================================
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: RISC V

Post by Shaos »

Shaos wrote:Строю модель RISC-V с нуля и проверяю в вериляторе - очень занимательно получается :)

Только у меня не настоящий риск, а типа как в микроконтроллерах PIC - только две ступени конвейера и каждая разделена на 4 части т.е. как бы одна команда за 4 такта выполняется (с перехлёстом на время вычитки следующей команды) - тут оно как бы естественно, так как у меня 8-битная шина данных и каждую 32-битную инструкцию можно прочитать только в 4 захода и я по ходу дела ещё и декодирую её по частям :oidea:
Вобщем так и получается - 4 такта на инструкцию (на самом деле каждая инструкция выполняется как минимум 6 тактов, но с учётом перехлёста в конвейере в среднем выходит 4), но команды, которые передают управление (меняют PC), попусту расходуют один такт т.к. решение, что надо передавать управление, делается в первом такте второй стадии конвейреа (т.е. на 5 такте команды) и конвейер уже вычитал один байт следующей команды. Команды из серии LOAD (загрузка данных из памяти) кроме того забирают 1, 2 или 4 дополнительных такта в зависимости от того читается ли из памяти один байт, полуслово или слово. Команды из серии STORE (сохранение данных в память) забирают на 1 такт больше (т.е. лишние 2,3 или 5 тактов в зависимости от того сохраняется ли один байт, полуслово или слово).
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

На данный момент прохожу 45 тестов на совместимость с RV32I из 55 (или другими словами 82%)

Вся логика-арифметика закончена - осталось реализовать системные регистры, прерывания и привелигированный режим...

Code: Select all

Check         I-ADD-01 ... OK
Check        I-ADDI-01 ... OK
Check         I-AND-01 ... OK
Check        I-ANDI-01 ... OK
Check       I-AUIPC-01 ... OK
Check         I-BEQ-01 ... OK
Check         I-BGE-01 ... OK
Check        I-BGEU-01 ... OK
Check         I-BLT-01 ... OK
Check        I-BLTU-01 ... OK
Check         I-BNE-01 ... OK
Check       I-CSRRC-01   FAIL
Check      I-CSRRCI-01   FAIL
Check       I-CSRRS-01   FAIL
Check      I-CSRRSI-01   FAIL
Check       I-CSRRW-01   FAIL
Check      I-CSRRWI-01   FAIL
Check I-DELAY_SLOTS-01 ... OK
Check      I-EBREAK-01   FAIL
Check       I-ECALL-01   FAIL
Check   I-ENDIANESS-01 ... OK
Check     I-FENCE.I-01 ... OK
Check             I-IO ... OK
Check         I-JAL-01 ... OK
Check        I-JALR-01 ... OK
Check          I-LB-01 ... OK
Check         I-LBU-01 ... OK
Check          I-LH-01 ... OK
Check         I-LHU-01 ... OK
Check         I-LUI-01 ... OK
Check          I-LW-01 ... OK
Check I-MISALIGN_JMP-01  FAIL
Check I-MISALIGN_LDST-01 FAIL
Check         I-NOP-01 ... OK
Check          I-OR-01 ... OK
Check         I-ORI-01 ... OK
Check     I-RF_size-01 ... OK
Check    I-RF_width-01 ... OK
Check       I-RF_x0-01 ... OK
Check          I-SB-01 ... OK
Check          I-SH-01 ... OK
Check         I-SLL-01 ... OK
Check        I-SLLI-01 ... OK
Check         I-SLT-01 ... OK
Check        I-SLTI-01 ... OK
Check       I-SLTIU-01 ... OK
Check        I-SLTU-01 ... OK
Check         I-SRA-01 ... OK
Check        I-SRAI-01 ... OK
Check         I-SRL-01 ... OK
Check        I-SRLI-01 ... OK
Check         I-SUB-01 ... OK
Check          I-SW-01 ... OK
Check         I-XOR-01 ... OK
Check        I-XORI-01 ... OK
--------------------------------
FAIL: 10/55
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

По результатам компиляции в iCEcube2 для FPGA iCE40UP5K:

Code: Select all

Final Design Statistics
    Number of LUTs      	:	2187
    Number of DFFs      	:	324
    Number of DFFs packed to IO	:	0
    Number of Carrys    	:	275
    Number of RAMs      	:	4
    Number of ROMs      	:	0
    Number of IOs       	:	35
    Number of GBIOs     	:	1
    Number of GBs       	:	4
    Number of WarmBoot  	:	0
    Number of PLLs      	:	0
    Number of I2Cs      	:	0
    Number of SPIs      	:	0
    Number of DSPs     	:	0
    Number of SBIOODs     	:	0
    Number of LEDDAIPs     	:	0
    Number of RGBADRVs     	:	0
    Number of LFOSCs     	:	0
    Number of HFOSCs     	:	0
    Number of FILTER_50NSs     	:	0
    Number of SPRAMs     	:	0
Device Utilization Summary
    LogicCells                  :	2265/5280
    PLBs                        :	344/660
    BRAMs                       :	4/30
    IOs and GBIOs               :	36/36
    PLLs                        :	0/1
    I2Cs                        :	0/2
    SPIs                        :	0/2
    DSPs                        :	0/8
    SBIOODs                     :	0/3
    RGBADRVs                    :	0/1
    LEDDAIPs                    :	0/1
    LFOSCs                      :	0/1
    HFOSCs                      :	0/1
    SPRAMs                      :	0/4
    FILTER50NSs                 :	0/2
#####################################################################
Placement Timing Summary
The timing summary is based on estimated routing delays after
placement. For final timing report, please carry out the timing
analysis after routing.
=====================================================================
#####################################################################
                     Clock Summary 
=====================================================================
Number of clocks: 1
Clock: retro|clk | Frequency: 27.45 MHz | Target: 48.36 MHz
=====================================================================
                     End of Clock Summary
#####################################################################
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 23989
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: 32-битный персональный компьютер nedoPC-5

Post by Shaos »

Исходник целиком:

Code: Select all

// retro.v - very simplistic implementation of RISC-V architecture RV32I
// that is compatible with Verilator and passing 45 RV32I compliance tests
// out of 55 (no CSRs, no priviledged mode, no traps etc)
//
// RETRO-V v1.0.0 (November 2018)
//
// Copyright 2018 Alexander Shabarshin <ashabarshin@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

module retro (nres,clk,hold,address,data_in,data_out,wren);

parameter ADDRESS_WIDTH = 16;       // address bus width
parameter REGISTERS_NUM = 32;       // number of registers
input nres;                         // external negative reset
input clk;                          // external clock
input hold;                         // external hold
output [ADDRESS_WIDTH-1:0] address; // address bus for external memory
input [7:0] data_in;                // data in for memory read
output reg [7:0] data_out;          // data out for memory write
output reg wren;                    // memory write enable
// special internal addresses:
parameter UART_TX_ADDR  = 32'h40002000;
parameter START_ADDR    = 32'h80000000;

// general purpose & control and status registers
reg [31:0] regs[REGISTERS_NUM-1:0] /* synthesis syn_ramstyle="block_ram" */;
reg [31:0] inst /*verilator public*/;
reg [31:0] arg1 /*verilator public*/;
reg [31:0] arg2 /*verilator public*/;
reg [31:0] imm  /*verilator public*/;
reg [31:0] pc;  // program counter
reg [31:0] pc2; // stored program counter
reg [31:0] res; // result register
reg [9:0] op;   // extended opcode
reg [4:0] rd,rd2; // destination register ids
reg [2:0] lbytes; // current number of bytes to load
reg [2:0] sbytes; // current number of bytes to store
reg [1:0] bytes;  // total number of bytes to transfer (3 means 4)
reg [31:0] extaddr; // external address
reg flag,pcflag,unflag,errop/*verilator public*/; // flags

assign address = (lbytes!=3'b0||sbytes!=3'b0)?extaddr[ADDRESS_WIDTH-1:0]:pc[ADDRESS_WIDTH-1:0];

always @(posedge clk) begin
     if(~hold) begin
        case (pc[1:0])

          2'b00: begin // 1st byte of the instruction
                 if(sbytes==3'b0 && lbytes==3'b0) begin
                    inst <= { 24'h000000, data_in };
                 end else begin
                    inst <= { 24'h000000, inst[7:0] };
                 end
                 // 2nd stage of pipeline below
                 if(op[6:0]==7'b0) begin
                    // in case of pipleine re-init
                    rd2 <= 5'b0;
                 end else begin
                    rd2 <= rd;
                 end
                 casez ( op )
                    10'b???0110111: // LUI
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= imm;
                       end
                    10'b???0010111: // AUIPC
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= pc2 + imm;
                       end
                    10'b???1101111: // JAL
                       begin
                               pc2 <= pc2 + imm;
                               pcflag <= 1'b1; // set pc-change event
                               op <= 10'b0; // !!!
                               if(rd!=5'b0) begin
                                  regs[rd] <= pc; // this is pc+4 stored to rd
                               end
                               res <= 32'b0;
                       end
                    10'b0001100111: // JALR
                       begin
                               pc2 <= arg1 + imm;
                               pcflag <= 1'b1; // set pc-change event
                               op <= 10'b0; // !!!
                               if(rd!=5'b0) begin
                                  regs[rd] <= pc; // this is pc+4 stored to rd
                               end
                               res <= 32'b0;
                       end
                    10'b0001100011: // BEQ
                       begin
                               if(arg1==arg2) begin
                                  pc2 <= pc2 + imm;
                                  pcflag <= 1'b1; // set pc-change event
                                  op <= 10'b0; // !!!
                               end else begin
                                  pc2 <= pc;
                                  pcflag <= 1'b0;
                               end
                               res <= 32'b0;
                       end
                    10'b0011100011: // BNE
                       begin
                               if(arg1!=arg2) begin
                                  pc2 <= pc2 + imm;
                                  pcflag <= 1'b1; // set pc-change event
                                  op <= 10'b0; // !!!
                               end else begin
                                  pc2 <= pc;
                                  pcflag <= 1'b0;
                               end
                               res <= 32'b0;
                       end
                    10'b1001100011: // BLT
                       begin
                               if($signed(arg1) < $signed(arg2)) begin
                                  pc2 <= pc2 + imm;
                                  pcflag <= 1'b1; // set pc-change event
                                  op <= 10'b0; // !!!
                               end else begin
                                  pc2 <= pc;
                                  pcflag <= 1'b0;
                               end
                               res <= 32'b0;
                       end
                    10'b1011100011: // BGE
                       begin
                               if($signed(arg1) >= $signed(arg2)) begin
                                  pc2 <= pc2 + imm;
                                  pcflag <= 1'b1; // set pc-change event
                                  op <= 10'b0; // !!!
                               end else begin
                                  pc2 <= pc;
                                  pcflag <= 1'b0;
                               end
                               res <= 32'b0;
                       end
                    10'b1101100011: // BLTU
                       begin
                               if(arg1 < arg2) begin
                                  pc2 <= pc2 + imm;
                                  pcflag <= 1'b1; // set pc-change event
                                  op <= 10'b0; // !!!
                               end else begin
                                  pc2 <= pc;
                                  pcflag <= 1'b0;
                               end
                               res <= 32'b0;
                       end
                    10'b1111100011: // BGEU
                       begin
                               if(arg1 >= arg2) begin
                                  pc2 <= pc2 + imm;
                                  pcflag <= 1'b1; // set pc-change event
                                  op <= 10'b0; // !!!
                               end else begin
                                  pc2 <= pc;
                                  pcflag <= 1'b0;
                               end
                               res <= 32'b0;
                       end
                    10'b???0?00011: // LOAD or STORE
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               if(sbytes==3'b0 && lbytes==3'b0) begin
                                 extaddr <= arg1+imm;
                                 if(op[5]==0) begin // LOAD
                                   lbytes <= (op[8:7]==2'b00)?3'b001:
                                             (op[8:7]==2'b01)?3'b010:
                                             (op[8:7]==2'b10)?3'b100:3'b0;
                                   bytes  <= (op[8:7]==2'b00)?2'b01:
                                             (op[8:7]==2'b01)?2'b10:
                                             (op[8:7]==2'b10)?2'b11:2'b0;
                                   unflag <= op[9];
                                 end else begin // STORE
                                   sbytes <= (op[8:7]==2'b00)?3'b010:
                                             (op[8:7]==2'b01)?3'b011:
                                             (op[8:7]==2'b10)?3'b101:3'b0;
                                   bytes  <= (op[8:7]==2'b00)?2'b01:
                                             (op[8:7]==2'b01)?2'b10:
                                             (op[8:7]==2'b10)?2'b11:2'b0;
                                 end
                                 res <= 32'b0;
                               end else begin // memory transfer in progress
                                 if(op[5]==0) begin // LOAD
                                   extaddr <= extaddr + 1'b1;
                                   lbytes <= lbytes - 1'b1;
                                   case (lbytes)
                                     3'b100:
                                       begin
                                         res <= { 24'b0, data_in };
                                       end
                                     3'b011:
                                       begin
                                         res <= { 16'b0, data_in, res[7:0] };
                                       end
                                     3'b010:
                                       begin
                                         if(bytes==2'b10) begin
                                            res <= { 24'b0, data_in };
                                         end else begin // 2'b11
                                            res <= { 8'b0, data_in, res[15:0] };
                                         end
                                       end
                                     default: // 3'b001
                                       begin
                                         if(bytes==2'b01) begin
                                            if(unflag==1'b1 || data_in[7]==1'b0) begin
                                               res <= { 24'b000000000000000000000000, data_in };
                                            end else begin
                                               res <= { 24'b111111111111111111111111, data_in };
                                            end
                                         end else begin
                                            if(bytes==2'b10) begin
                                               if(unflag==1'b1 || data_in[7]==1'b0) begin
                                                  res <= { 16'b0000000000000000, data_in, res[7:0] };
                                               end else begin
                                                  res <= { 16'b1111111111111111, data_in, res[7:0] };
                                               end
                                            end else begin // 2'b11
                                               res <= { data_in, res[23:0] };
                                            end
                                         end
                                       end
                                   endcase
                                 end else begin // STORE
                                   sbytes <= sbytes - 1'b1;
                                   case (sbytes)
                                     3'b101:
                                       begin
                                        extaddr <= extaddr;
                                        data_out <= arg2[7:0];
                                        wren <= 1'b1;
                                       end
                                     3'b100:
                                       begin
                                        extaddr <= extaddr+1'b1;
                                        data_out <= arg2[15:8];
                                        wren <= 1'b1;
                                       end
                                     3'b011:
                                       begin
                                        extaddr <= extaddr+{31'b0,bytes[0]};
                                        data_out <= (bytes==2'b11)?arg2[23:16]:arg2[7:0];
                                        wren <= 1'b1;
                                       end
                                     3'b010:
                                       begin
                                        extaddr <= (extaddr==UART_TX_ADDR)?32'hFFFFFFFF:(extaddr+{31'b0,bytes[1]});
                                        data_out <= (bytes==2'b11)?arg2[31:24]:(bytes==2'b10)?arg2[15:8]:arg2[7:0];
                                        wren <= 1'b1;
                                       end
                                     default:
                                       begin
                                        extaddr <= 32'b0;
                                        data_out <= data_out;
                                        wren <= 1'b0;
                                       end
                                   endcase
                                   res <= 32'b0;
                                 end
                               end
                       end
                    10'b0000010011: // ADDI
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= arg1 + imm;
                       end
                    10'b0100010011: // SLTI
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= ($signed(arg1) < $signed(imm))?32'b1:32'b0;
                       end
                    10'b0110010011: // SLTIU
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= (arg1 < imm)?32'b1:32'b0;
                       end
                    10'b1000010011: // XORI
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= arg1 ^ imm;
                       end
                    10'b1100010011: // ORI
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= arg1 | imm;
                       end
                    10'b1110010011: // ANDI
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= arg1 & imm;
                       end
                    10'b0010010011, // SLLI
                    10'b0010110011: // SLL
                       begin
                          pc2 <= pc;
                          pcflag <= 1'b0;
                          case (arg2[4:0])
                            5'b00000: res <= arg1;
                            5'b00001: res <= { arg1[30:0], 1'b0 };
                            5'b00010: res <= { arg1[29:0], 2'b0 };
                            5'b00011: res <= { arg1[28:0], 3'b0 };
                            5'b00100: res <= { arg1[27:0], 4'b0 };
                            5'b00101: res <= { arg1[26:0], 5'b0 };
                            5'b00110: res <= { arg1[25:0], 6'b0 };
                            5'b00111: res <= { arg1[24:0], 7'b0 };
                            5'b01000: res <= { arg1[23:0], 8'b0 };
                            5'b01001: res <= { arg1[22:0], 9'b0 };
                            5'b01010: res <= { arg1[21:0], 10'b0 };
                            5'b01011: res <= { arg1[20:0], 11'b0 };
                            5'b01100: res <= { arg1[19:0], 12'b0 };
                            5'b01101: res <= { arg1[18:0], 13'b0 };
                            5'b01110: res <= { arg1[17:0], 14'b0 };
                            5'b01111: res <= { arg1[16:0], 15'b0 };
                            5'b10000: res <= { arg1[15:0], 16'b0 };
                            5'b10001: res <= { arg1[14:0], 17'b0 };
                            5'b10010: res <= { arg1[13:0], 18'b0 };
                            5'b10011: res <= { arg1[12:0], 19'b0 };
                            5'b10100: res <= { arg1[11:0], 20'b0 };
                            5'b10101: res <= { arg1[10:0], 21'b0 };
                            5'b10110: res <= { arg1[9:0], 22'b0 };
                            5'b10111: res <= { arg1[8:0], 23'b0 };
                            5'b11000: res <= { arg1[7:0], 24'b0 };
                            5'b11001: res <= { arg1[6:0], 25'b0 };
                            5'b11010: res <= { arg1[5:0], 26'b0 };
                            5'b11011: res <= { arg1[4:0], 27'b0 };
                            5'b11100: res <= { arg1[3:0], 28'b0 };
                            5'b11101: res <= { arg1[2:0], 29'b0 };
                            5'b11110: res <= { arg1[1:0], 30'b0 };
                            5'b11111: res <= { arg1[0], 31'b0 };
                          endcase
                       end
                    10'b1010010011, // SRLI or SRAI
                    10'b1010110011: // SRL or SRA
                       begin
                       pc2 <= pc;
                       pcflag <= 1'b0;
                       if(flag==1'b0 || arg1[31]==1'b0)
                       begin
                          case (arg2[4:0])
                            5'b00000: res <= arg1;
                            5'b00001: res <= { 1'b0, arg1[31:1] };
                            5'b00010: res <= { 2'b0, arg1[31:2] };
                            5'b00011: res <= { 3'b0, arg1[31:3] };
                            5'b00100: res <= { 4'b0, arg1[31:4] };
                            5'b00101: res <= { 5'b0, arg1[31:5] };
                            5'b00110: res <= { 6'b0, arg1[31:6] };
                            5'b00111: res <= { 7'b0, arg1[31:7] };
                            5'b01000: res <= { 8'b0, arg1[31:8] };
                            5'b01001: res <= { 9'b0, arg1[31:9] };
                            5'b01010: res <= { 10'b0, arg1[31:10] };
                            5'b01011: res <= { 11'b0, arg1[31:11] };
                            5'b01100: res <= { 12'b0, arg1[31:12] };
                            5'b01101: res <= { 13'b0, arg1[31:13] };
                            5'b01110: res <= { 14'b0, arg1[31:14] };
                            5'b01111: res <= { 15'b0, arg1[31:15] };
                            5'b10000: res <= { 16'b0, arg1[31:16] };
                            5'b10001: res <= { 17'b0, arg1[31:17] };
                            5'b10010: res <= { 18'b0, arg1[31:18] };
                            5'b10011: res <= { 19'b0, arg1[31:19] };
                            5'b10100: res <= { 20'b0, arg1[31:20] };
                            5'b10101: res <= { 21'b0, arg1[31:21] };
                            5'b10110: res <= { 22'b0, arg1[31:22] };
                            5'b10111: res <= { 23'b0, arg1[31:23] };
                            5'b11000: res <= { 24'b0, arg1[31:24] };
                            5'b11001: res <= { 25'b0, arg1[31:25] };
                            5'b11010: res <= { 26'b0, arg1[31:26] };
                            5'b11011: res <= { 27'b0, arg1[31:27] };
                            5'b11100: res <= { 28'b0, arg1[31:28] };
                            5'b11101: res <= { 29'b0, arg1[31:29] };
                            5'b11110: res <= { 30'b0, arg1[31:30] };
                            5'b11111: res <= { 31'b0, arg1[31] };
                          endcase
                       end else begin
                          case (arg2[4:0])
                            5'b00000: res <= arg1;
                            5'b00001: res <= {  1'b1, arg1[31:1] };
                            5'b00010: res <= {  2'b11, arg1[31:2] };
                            5'b00011: res <= {  3'b111, arg1[31:3] };
                            5'b00100: res <= {  4'b1111, arg1[31:4] };
                            5'b00101: res <= {  5'b11111, arg1[31:5] };
                            5'b00110: res <= {  6'b111111, arg1[31:6] };
                            5'b00111: res <= {  7'b1111111, arg1[31:7] };
                            5'b01000: res <= {  8'b11111111, arg1[31:8] };
                            5'b01001: res <= {  9'b111111111, arg1[31:9] };
                            5'b01010: res <= { 10'b1111111111, arg1[31:10] };
                            5'b01011: res <= { 11'b11111111111, arg1[31:11] };
                            5'b01100: res <= { 12'b111111111111, arg1[31:12] };
                            5'b01101: res <= { 13'b1111111111111, arg1[31:13] };
                            5'b01110: res <= { 14'b11111111111111, arg1[31:14] };
                            5'b01111: res <= { 15'b111111111111111, arg1[31:15] };
                            5'b10000: res <= { 16'b1111111111111111, arg1[31:16] };
                            5'b10001: res <= { 17'b11111111111111111, arg1[31:17] };
                            5'b10010: res <= { 18'b111111111111111111, arg1[31:18] };
                            5'b10011: res <= { 19'b1111111111111111111, arg1[31:19] };
                            5'b10100: res <= { 20'b11111111111111111111, arg1[31:20] };
                            5'b10101: res <= { 21'b111111111111111111111, arg1[31:21] };
                            5'b10110: res <= { 22'b1111111111111111111111, arg1[31:22] };
                            5'b10111: res <= { 23'b11111111111111111111111, arg1[31:23] };
                            5'b11000: res <= { 24'b111111111111111111111111, arg1[31:24] };
                            5'b11001: res <= { 25'b1111111111111111111111111, arg1[31:25] };
                            5'b11010: res <= { 26'b11111111111111111111111111, arg1[31:26] };
                            5'b11011: res <= { 27'b111111111111111111111111111, arg1[31:27] };
                            5'b11100: res <= { 28'b1111111111111111111111111111, arg1[31:28] };
                            5'b11101: res <= { 29'b11111111111111111111111111111, arg1[31:29] };
                            5'b11110: res <= { 30'b111111111111111111111111111111, arg1[31:30] };
                            5'b11111: res <= { 31'b1111111111111111111111111111111, arg1[31] };
                          endcase
                       end
                       end
                    10'b0000110011: // ADD or SUB
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               if(flag==1'b0) begin
                                 res <= arg1 + arg2;
                               end else begin
                                 res <= arg1 - arg2;
                               end
                       end
                    10'b0100110011: // SLT
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= ($signed(arg1) < $signed(arg2))?1:0;
                       end
                    10'b0110110011: // SLTU
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= (arg1 < arg2)?1:0;
                       end
                    10'b1000110011: // XOR
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= arg1 ^ arg2;
                       end
                    10'b1100110011: // OR
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= arg1 | arg2;
                       end
                    10'b1110110011: // AND
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= arg1 & arg2;
                       end
                    10'b0001110011: // ECALL, EBREAK and other priviledged instructions
                       begin
                               pc2 <= pc;
                               pcflag <= 1'b0;
                               res <= 32'b0;
                       end
                    default: // FENCE and FENCE.I go here as NOPs
                       begin
                               pc2 <= pc; // this is actually store in the 1st stage to use in the 2nd
                               pcflag <= 1'b0;
                               res <= 32'b0;
                       end
                 endcase
                 end

          2'b01: begin // 2nd byte of the instruction (fill op and rd)
                 inst <= { 16'h0000, data_in, inst[7:0] };
                 if(inst[6:0]==7'b0100011 || inst[6:0]==7'b1100011) begin
                    rd <= 5'b0; // STORE and BRANCH special case
                 end else begin
                    rd <= { data_in[3:0], inst[7] };
                 end
                 flag <= data_in[7];
                 op <= { data_in[6:4], inst[6:0] };
                 // check if it's a valid opcode or not
                 case ( inst[6:0] )
                     7'b0110111,7'b0010111,7'b1101111,7'b0010011,7'b0110011:
                       begin
                         errop <= 1'b0;
                       end
                     7'b1100111: // JALR
                       begin
                         if(data_in[6:4]==3'b000) begin
                            errop <= 1'b0;
                         end else begin
                            errop <= 1'b1;
                         end
                       end
                     7'b1100011: // BRANCH
                       begin
                         if(data_in[6:4]==3'b010 || data_in[6:4]==3'b011) begin
                            errop <= 1'b1;
                         end else begin
                            errop <= 1'b0;
                         end
                       end
                     7'b0000011: // LOAD
                       begin
                         if(data_in[6:4]==3'b000 || data_in[6:4]==3'b001 || data_in[6:4]==3'b010 ||
                            data_in[6:4]==3'b100 || data_in[6:4]==3'b101) begin
                            errop <= 1'b0;
                         end else begin
                            errop <= 1'b1;
                         end
                       end
                     7'b0100011: // SAVE
                       begin
                         if(data_in[6:4]==3'b000 || data_in[6:4]==3'b001 || data_in[6:4]==3'b010) begin
                            errop <= 1'b0;
                         end else begin
                            errop <= 1'b1;
                         end
                       end
                     7'b0001111: // MISC-MEM (FENCE and FENCE.I)
                       begin
                         if(data_in[6:4]==3'b000 || data_in[6:4]==3'b001) begin
                            errop <= 1'b0;
                         end else begin
                            errop <= 1'b1;
                         end
                       end
                     7'b1110011: // SYSTEM
                       begin
                         if(data_in[6:4]==3'b100) begin
                            errop <= 1'b1;
                         end else begin
                            errop <= 1'b0;
                         end
                       end
                     default: // everything else is invalid
                       begin
                         errop <= 1'b1;
                       end
                 endcase
                 // 2nd step of 2nd stage of pipeline below
                 if(rd2!=5'b0) begin // it has to be rd2 otherwise it wastes 1K+ LUTs in iCEcube2
                     regs[rd2] <= res; // write back
                 end
                 end

          2'b10: begin // 3rd byte of the instruction (fill arg1)
                 inst <= { 8'h00, data_in, inst[15:0] };
                 casez ( op )
                     10'b0001100111,
                     10'b???1100011,
                     10'b???0000011,
                     10'b???0100011,
                     10'b???0010011,
                     10'b???0110011,
                     10'b0011110011,
                     10'b0101110011,
                     10'b0111110011:
                        arg1 <= regs[ { data_in[3:0], flag } ]; // value from rs1
                     10'b1011110011,
                     10'b1101110011,
                     10'b1111110011:
                        arg1 <= { 27'b0, data_in[3:0], flag }; // value of rs1
                     default:
                        arg1 <= 32'h00000000;
                 endcase
                 arg2 <= { 28'b0, data_in[7:4] };
                 end

          2'b11: begin // 4th byte of the instruction (fill arg2, imm and flag)
                 inst <= { data_in, inst[23:0] }; // inst can not be used anymore
                 flag <= data_in[6]; // subfunction flag
                 casez ( op )
                     10'b???0110111,10'b???0010111:
                        begin // U-type
                          imm <= { data_in, inst[23:12], 12'b0 }; // 32-bit immeadiate (with zeroed lowest 12 bits)
                          arg2 <= 32'h00000000;
                        end
                     10'b???1101111:
                        begin // J-type
                          if(data_in[7]) begin
                            imm <= { 12'b111111111111, inst[19:12], inst[20], data_in[6:0], inst[23:21], 1'b0 }; // 21-bit immediate (negative)
                          end else begin
                            imm <= { 12'b000000000000, inst[19:12], inst[20], data_in[6:0], inst[23:21], 1'b0 }; // 21-bit immediate (positive)
                          end
                          arg2 <= 32'h00000000;
                        end
                     10'b???1100011:
                        begin // B-type
                          if(data_in[7]) begin
                            imm <= { 20'b11111111111111111111, inst[7], data_in[6:1], inst[11:8], 1'b0 }; // 13-bit immediate (negative)
                          end else begin
                            imm <= { 20'b00000000000000000000, inst[7], data_in[6:1], inst[11:8], 1'b0 }; // 13-bit immediate (negative)
                          end
                          arg2 <= regs[ { data_in[0], arg2[3:0] } ]; // value from rs2
                        end
                     10'b???0100011:
                        begin // S-type
                          if(data_in[7]) begin
                            imm <= { 21'b111111111111111111111, data_in[6:1], inst[11:7] }; // 12-bit immediate (negative)
                          end else begin
                            imm <= { 21'b000000000000000000000, data_in[6:1], inst[11:7] }; // 12-bit immediate (positive)
                          end
                          arg2 <= regs[ { data_in[0], arg2[3:0] } ]; // value from rs2
                        end
                     10'b0010010011,10'b1010010011:
                        begin // shifts
                          imm <= 32'h00000000;
                          arg2 <= { 27'b0, data_in[0], arg2[3:0] }; // shamt
                        end
                     10'b???0110011:
                        begin // R-type
                          imm <= 32'h00000000;
                          arg2 <= regs[ { data_in[0], arg2[3:0] } ]; // value from rs2
                        end
                     default: // I-type
                        begin
                          if(data_in[7]) begin
                            imm <= { 20'b11111111111111111111, data_in, arg2[3:0] }; // 12-bit immediate or CSR (negative)
                          end else begin
                            imm <= { 20'b00000000000000000000, data_in, arg2[3:0] }; // 12-bit immediate or CSR (positive)
                          end
                          arg2 <= 32'h00000000;
                        end
                 endcase
                 end
        endcase
     end

end

always @(negedge clk) begin
  if(nres) begin
     if(~hold) begin
        if(pcflag) begin
           pc <= pc2 & 32'hFFFFFFFC;
        end else if(sbytes==3'b0 && lbytes==3'b0) begin
           pc <= pc + 1'b1;
        end
     end
  end else begin
     // reset condition (nres=0)
     pc <= START_ADDR;
  end
end

initial begin
regs[0]=32'h00000000; // ???
data_out=8'h00;
wren=1'b0;
sbytes=3'b0;
lbytes=3'b0;
end

endmodule
В таком виде этого уже достаточно, чтобы построить простой ретро-компьютер, программы под который пишутся на GCC (32-битный, но без прерываний и таймеров)
Я тут за главного - если что шлите мыло на me собака shaos точка net