Merge pull request #175 from mithro/cpu-docs

Standardizing `cpu_variants` and adding lots of documentation
This commit is contained in:
enjoy-digital 2019-04-27 21:24:06 +02:00 committed by GitHub
commit 2d5bae3def
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 561 additions and 35 deletions

9
doc/BIOS.md Normal file
View file

@ -0,0 +1,9 @@
LiteX takes the philosophy of pushing as much onto the [[Soft CPU]] as possible. This means that most [[Gateware]] needs some accompanying code to initialize various settings and configurations. This specialized firmware is called the [[BIOS]].
The [[BIOS]] generally does tasks like;
* Train any external DDR memory
* Load the user's [[Firmware]] into external memory via;
* Communication channels like serial, tftp
* Other storage systems like external flash.
This can make it very fast to do development as you can iterate on [[Firmware]] development quickly.

114
doc/Firmware.md Normal file
View file

@ -0,0 +1,114 @@
# Firmware
LiteX is normally used with custom firmware running on the Soft-CPU inside the gateware (although in some cases it can be used with software running on a computer connected to the FPGA).
This firmware can either be custom bare metal C code or be built on top of an existing real time operating system or even full operating system. All Soft-CPUs let you write your own bare metal firmware, but only some configurations work with operating systems.
[Firmware which runs on LiteX Google Doc](https://docs.google.com/document/d/11uzjWRWk9-KuBFc7chUNUluL5ajysY2qfXvt1vttl7k/edit)
# Baremetal
## Support Soft-CPU Configurations
**All** Soft-CPUs let you write your own bare metal firmware.
* `lm32` (any variant)
* `or1k` (any variant)
* `vexriscv` (any variant)
* `picorv32` (any variant)
* `minerva` (any variant)
---
# MicroPython via [FuPy Project](https://fupy.github.io)
From the FuPy website;
> The aim of this project is to make MicroPython run on FPGAs using the LiteX & Migen+MiSoC technologies. This allows you to do full stack development (FPGA gateware & soft CPU firmware) in Python!
## When to use?
MicroPython is great to use when ease and speed of development is more important than performance. When coupled with LiteX you can push MicroPython even further by moving performance critical parts into FPGA gateware.
## Hardware Requirements
MicroPython is very light on requirements;
* 32 kilobytes memory - can normally use internal block ram inside FPGA.
* 128 kilobytes of storage - can normally use spare space in SPI flash used to configure the FPGA.
## Support Soft-CPU Configurations
* `lm32` (any variant), in FuPy repository
* `or1k` (any variant), in FuPy repository
* (in progress) `vexriscv` (any variant), in FuPy repository
* (in progress) `picorv32` (any variant), in FuPy repository
---
# [NuttX](http://www.nuttx.org/)
From the NuttX website;
> NuttX is a real-time operating system (RTOS) with an emphasis on standards compliance and small footprint. Scalable from 8-bit to 32-bit microcontroller environments, the primary governing standards in NuttX are Posix and ANSI standards. Additional standard APIs from Unix and other common RTOS's (such as VxWorks) are adopted for functionality not available under these standards, or for functionality that is not appropriate for deeply-embedded environments (such as fork()).
## When to use?
NuttX is a good option if you are already using NuttX or need to use a function that is already available in the NuttX ecosystem.
## Hardware Requirements
Unknown.
LiteEth networking is supported.
## Support Soft-CPU Configurations
* `lm32` (any variant), in upstream repository
---
# [Zephyr](https://www.zephyrproject.org/)
From the Zephyr website;
> The Zephyr Project is a scalable real-time operating system (RTOS) supporting multiple hardware architectures, optimized for resource constrained devices, and built with safety and security in mind.
## When to use?
Zephyr is a great choice if you don't want to write your own bare metal firmware. It is under active development and moving forward quickly.
It also has a coding style which makes it easy to move to Linux at a later state if you outgrow the abilities of Zephyr.
## Hardware Requirements
Zephyr is very light on requirements;
* 32 kilobytes memory - can normally use internal block ram inside FPGA.
* Some storage - can normally use spare space in SPI flash used to configure the FPGA.
## Support Soft-CPU Configurations
* `vexriscv` (any variant), in upstream repository
* `picorv32` (any variant), out of tree port
---
# [Linux](https://en.wikipedia.org/wiki/Linux)
From Wikipedia;
> Linux is a family of free and open-source software operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991 by Linus Torvalds. ...
> Linux also runs on embedded systems, i.e. devices whose operating system is typically built into the firmware and is highly tailored to the system. This includes routers, automation controls, televisions, digital video recorders, video game consoles, and smartwatches. Many smartphones and tablet computers run Android and other Linux derivatives.[30] Because of the dominance of Android on smartphones, Linux has the largest installed base of all general-purpose operating systems.
## When to use?
Linux tends to run quite slowly on the soft-CPUs supported by LiteX and needs hardware acceleration to do most useful operations. Linux makes the most sense were the existing large pool of Linux drivers or software is helpful.
## Hardware Requirements
Linux operating support generally needs;
* Large and fast FPGA, something like Lattice ECP5 or Xilinx Artix 7 / Spartan 6.
* 32+ megabytes memory, generally meaning external SDRAM or DDR RAM.
* UART serial console.
* Fast Ethernet (100 Megabit or 1 Gigabit Ethernet).
* Some type of storage, large SPI flash or SD Card or SATA hard drives potential options.
## Support Soft-CPU Configurations
* `or1k` (`linux` variant), out of tree port being upstreamed
* `vexriscv` (`linux` variant), out of tree port being upstreamed

254
doc/Soft-CPU.md Normal file
View file

@ -0,0 +1,254 @@
All LiteX SoCs need some type of CPU to operate correctly. Most use an "Soft CPU" embedded in the gateware for this purpose, but in some cases a host computer is used instead (for example this can be true in the PCIe card case).
# Summary of Soft CPUs
Currently the supported Soft CPUs are:
* [`lm32`](https://github.com/enjoy-digital/litex/tree/master/litex/soc/cores/cpu/lm32) -- a [LatticeMico32](https://en.wikipedia.org/wiki/LatticeMico32) soft core.
* [`or1k`](https://github.com/enjoy-digital/litex/tree/master/litex/soc/cores/cpu/mor1kx) -- an [OpenRISC 1000](https://openrisc.io/or1k.html) soft core (see also [Open RISC on Wikipedia](https://en.wikipedia.org/wiki/OpenRISC)).
* [`picorv32`](https://github.com/enjoy-digital/litex/tree/master/litex/soc/cores/cpu/picorv32) -- a [Small RISC V core by Clifford Wolf](https://github.com/cliffordwolf/picorv32), implementing the `rv32imc` instruction set (or configured subsets)
* [`vexriscv`](https://github.com/enjoy-digital/litex/tree/master/litex/soc/cores/cpu/vexriscv) -- an [FPGA Friendly RISC V core by SpinalHDL](https://github.com/SpinalHDL/VexRiscv), implementing the `rv32im` instruction set (hardware multiply optional)
* [`minerva`](https://github.com/enjoy-digital/litex/tree/master/litex/soc/cores/cpu/minerva) -- an Minerva is a CPU core that currently implements the RISC-V RV32I instruction set and its microarchitecture is described in plain Python code using the [nMigen toolbox](https://github.com/m-labs/nmigen).
# Soft CPU Variants
Most of these CPUs have multiple configuration "variants" which customize the configuration to target a specific type of firmware and performance. All these CPUs can be used with your own bare metal firmware.
## `minimal`
Aliases: `min`
Minimal is the smallest possible working configuration for a given CPU type. These features frequently disables a large number of useful such as illegal instruction exceptions and similar. It should only be used if the absolute smallest configuration is needed.
### Supported CPUs
* lm32
* picorv32
* vexriscv
## `lite`
**Aliases**: `zephyr`, `nuttx`, `light`
Lite is the configuration which should work okay for bare metal firmware and RTOS like NuttX or Zephyr on small big FPGAs like the Lattice iCE40 parts. It can also be used for designs which are more resource constrained.
### Recommended FPGAs
* Lattice iCE40 Series - iCE40HX, iCE40LP, iCE40UP5K
* Any resource constrained design.
### Supported CPUs
* lm32
* vexriscv
## `standard`
**Aliases**: `std`
Standard is the default configuration which should work well for bare metal firmware and RTOS like NuttX or Zephyr on modern big FPGAs.
### Supported CPUs
* lm32
* minerva
* picorv32
* or1k
* vexriscv
### Recommended FPGAs
* Xilinx Series 7 - Artix 7, Kintex 7, Spartan 7
* Xilinx Spartan 6
* Lattice ECP5
## `linux`
This target enables CPU features such as MMU that are required to get Linux booting.
### Supported CPUs
* or1k
* vexriscv
---
# Soft CPU Extensions
Extensions are added to the CPU variant with a `+`. For example a `minimal` variant with the `debug` extension would be `minimal+debug`.
## `debug`
The debug extension enables extra features useful for debugging. This normally includes things like JTAG port.
### Supported CPUs
* vexriscv
## TODO - `mmu`
The `mmu` extension enables a memory protection unit.
### Supported CPUs
* lm32 (untested)
* vexriscv
* or1k
## TODO - `hmul`
The `hmul` extension enables hardware multiplication acceleration.
---
# Binutils + Compiler
* lm32 support was added to upstream GCC around ~2009, no clang support.
* or1k support was added to upstream GCC in version 9.0.0, clang support was added upstream in version XXX
* riscv support (VexRISCV, PicoRV32 and Minerva) was added to upstream GCC in version 7.1.0, clang support was added upstream in version 3.1
You can compile your own compiler, download a precompiled toolchain or use an environment like [TimVideos LiteX BuildEnv](https://github.com/timvideos/litex-buildenv/) which provides precompiled toolchain for all three architectures.
Note: RISC-V toolchains support or require various extensions. Generally `rv32i` is used on smaller FPGAs, and `rv32im` on larger FPGAs -- the `rv32im` adds hardware multiplication and division (see [RISC V ISA base and extensions on Wikipedia](https://en.wikipedia.org/wiki/RISC-V#ISA_base_and_extensions) for more detail).
---
# SoftCPU options
## lm32 - [LatticeMico32](https://github.com/m-labs/lm32)
[LatticeMico32](https://en.wikipedia.org/wiki/LatticeMico32) soft core, small and designed for an FPGA.
### CPU Variants
* minimal
* lite
* standard
### Tooling support
* Upstream GCC
* Upstream Binutils
### OS Support
* No upstream Linux, very old Linux port
* Upstream NuttX
* No Zephyr support
### Community
* No current new activity
---
## [mor1k - OpenRISC](https://github.com/openrisc/mor1kx)
An [OpenRISC 1000](https://openrisc.io/or1k.html) soft core (see also [Open RISC on Wikipedia](https://en.wikipedia.org/wiki/OpenRISC)).
### CPU Variants
* standard
* linux
### Tooling support
* Upstream GCC
* Upstream Binutils
* Upstream clang
### OS support
* No Zephyr support
* No NuttX support
* Upstream Linux
### Community
* Reasonable amount of activity.
---
## RISC-V - [VexRiscv](https://github.com/SpinalHDL/VexRiscv)
A [FPGA Friendly RISC V core by SpinalHDL](https://github.com/SpinalHDL/VexRiscv), implementing the `rv32im` instruction set (hardware multiply optional).
### CPU Variants
* minimal
* minimal_debug
* lite
* lite_debug
* standard
* standard_debug
* linux
### Tooling support
* Upstream GCC
* Upstream Binutils
* Upstream clang
### OS support
* Upstream Zephyr
* Unknown NuttX support
* Upstream Linux (in progress)
### Community
* Lots of current activity
* Currently supported under both LiteX & MiSoC
## RISC-V - [picorv32](https://github.com/cliffordwolf/picorv32/)
A [small RISC V core by Clifford Wolf](https://github.com/cliffordwolf/picorv32), implementing the `rv32imc` instruction set (or configured subsets).
### CPU Variants
* minimal
* standard
### Tooling support
* Upstream GCC
* Upstream Binutils
* Upstream clang
### OS support
* Out of tree Zephyr
* Unknown NuttX support
* Too small for Linux
### Community
* Some activity
## RISC-V - [`minerva`](https://github.com/enjoy-digital/litex/tree/master/litex/soc/cores/cpu/minerva)
The Minerva is a CPU core that currently implements the RISC-V RV32I instruction set and its microarchitecture is described in plain Python code using the [nMigen toolbox](https://github.com/m-labs/nmigen).
### CPU Variants
* standard
### Tooling support
* Upstream GCC
* Upstream Binutils
* Upstream clang
### OS support
* Unknown Zephyr support
* Unknown NuttX support
* Unknown Linux support
### Community
* Some activity

View file

@ -0,0 +1,14 @@
#!/bin/bash
# Soft-CPU - Description about supported Soft-CPUs.
# BIOS - Information about the BIOS and what it is used for.
# Firmware - Information about compatible Firmware.
for page in Soft-CPU BIOS Firmware; do
curl https://raw.githubusercontent.com/wiki/timvideos/litex-buildenv/${page}.md > ${page}.md
git add ${page}.md
done
GIT_MSG=$(tempfile) || exit
trap "rm -f -- '$GIT_MSG'" EXIT
git commit --message "Updating documents from LiteX BuildEnv Wiki"

View file

@ -12,8 +12,8 @@ class LM32(Module):
gcc_flags = "-mbarrel-shift-enabled -mmultiply-enabled -mdivide-enabled -msign-extend-enabled"
linker_output_format = "elf32-lm32"
def __init__(self, platform, eba_reset, variant=None):
assert variant in (None, "lite", "minimal"), "Unsupported variant %s" % variant
def __init__(self, platform, eba_reset, variant="standard"):
assert variant in ("standard", "lite", "minimal"), "Unsupported variant %s" % variant
self.reset = Signal()
self.ibus = i = wishbone.Interface()
self.dbus = d = wishbone.Interface()
@ -66,7 +66,7 @@ class LM32(Module):
self.add_sources(platform, variant)
@staticmethod
def add_sources(platform, variant=None):
def add_sources(platform, variant):
vdir = os.path.join(
os.path.abspath(os.path.dirname(__file__)), "verilog")
platform.add_sources(os.path.join(vdir, "submodule", "rtl"),
@ -93,5 +93,7 @@ class LM32(Module):
platform.add_verilog_include_path(os.path.join(vdir, "config_minimal"))
elif variant == "lite":
platform.add_verilog_include_path(os.path.join(vdir, "config_lite"))
else:
elif variant == "standard":
platform.add_verilog_include_path(os.path.join(vdir, "config"))
else:
raise TypeError("Unknown variant {}".format(variant))

View file

@ -12,8 +12,8 @@ class Minerva(Module):
gcc_flags = "-D__minerva__ -march=rv32i -mabi=ilp32"
linker_output_format = "elf32-littleriscv"
def __init__(self, platform, cpu_reset_address, variant=None):
assert variant is None, "Unsupported variant %s" % variant
def __init__(self, platform, cpu_reset_address, variant="standard"):
assert variant is "standard", "Unsupported variant %s" % variant
self.reset = Signal()
self.ibus = wishbone.Interface()
self.dbus = wishbone.Interface()

View file

@ -1,3 +1,4 @@
#!/usr/bin/env python3
import os
from migen import *
@ -12,8 +13,8 @@ class MOR1KX(Module):
gcc_flags = "-mhard-mul -mhard-div -mror"
linker_output_format = "elf32-or1k"
def __init__(self, platform, reset_pc, variant=None):
assert variant in (None, "linux"), "Unsupported variant %s" % variant
def __init__(self, platform, reset_pc, variant="standard"):
assert variant in ("standard", "linux"), "Unsupported variant %s" % variant
self.reset = Signal()
self.ibus = i = wishbone.Interface()
self.dbus = d = wishbone.Interface()
@ -47,7 +48,7 @@ class MOR1KX(Module):
p_DBUS_WB_TYPE="B3_REGISTERED_FEEDBACK",
)
if variant == None:
if variant == "standard":
# Use the default configuration
pass
elif variant == "linux":

View file

@ -1,3 +1,4 @@
#!/usr/bin/env python3
import os
from migen import *
@ -12,7 +13,7 @@ class PicoRV32(Module):
gcc_flags_template = "-D__picorv32__ -mno-save-restore -march=rv32{ext} -mabi=ilp32"
linker_output_format = "elf32-littleriscv"
def __init__(self, platform, progaddr_reset, variant):
def __init__(self, platform, progaddr_reset, variant="standard"):
self.gcc_flags = ""
self.reset = Signal()
@ -61,7 +62,7 @@ class PicoRV32(Module):
"p_STACKADDR" : 0xffffffff
}
if variant == None:
if variant == "standard":
self.gcc_flags = PicoRV32.gcc_flags_template.format(ext="im")
elif variant == "minimal":
picorv32_params.update({

View file

@ -6,18 +6,51 @@ from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
CPU_VARIANTS = {
"minimal": "VexRiscv_Min",
"minimal+debug": "VexRiscv_MinDebug",
"lite": "VexRiscv_Lite",
"lite+debug": "VexRiscv_LiteDebug",
"standard": "VexRiscv",
"standard+debug": "VexRiscv_Debug",
"full": "VexRiscv_Full",
"full+debug": "VexRiscv_FullDebug",
"linux": "VexRiscv_Linux",
}
GCC_FLAGS = {
# /-------- Base ISA
# |/------- Hardware Multiply + Divide
# ||/----- Atomics
# |||/---- Compressed ISA
# ||||/--- Single-Precision Floating-Point
# |||||/-- Double-Precision Floating-Point
# imacfd
"minimal": "-march=rv32i -mabi=ilp32",
"minimal+debug": "-march=rv32i -mabi=ilp32",
"lite": "-march=rv32i -mabi=ilp32",
"lite+debug": "-march=rv32i -mabi=ilp32",
"standard": "-march=rv32im -mabi=ilp32",
"standard+debug": "-march=rv32im -mabi=ilp32",
# Does full have floating point? - Add -march=fd, and -mabi=fd
"full": "-march=rv32imac -mabi=ilp32",
"full+debug": "-march=rv32imac -mabi=ilp32",
"linux": "-march=rv32imac -mabi=ilp32",
}
class VexRiscv(Module, AutoCSR):
name = "vexriscv"
endianness = "little"
gcc_triple = ("riscv64-unknown-elf", "riscv32-unknown-elf")
gcc_flags = "-D__vexriscv__ -march=rv32im -mabi=ilp32"
linker_output_format = "elf32-littleriscv"
def __init__(self, platform, cpu_reset_address, variant=None):
variant = "std" if variant is None else variant
variant = "std_debug" if variant == "debug" else variant
variants = ("std", "std_debug", "lite", "lite_debug", "min", "min_debug", "full", "full_debug")
assert variant in variants, "Unsupported variant %s" % variant
def __init__(self, platform, cpu_reset_address, variant="standard"):
assert variant in CPU_VARIANTS, "Unsupported variant %s" % variant
self.gcc_flags = GCC_FLAGS[variant]
self.platform = platform
self.variant = variant
self.external_variant = None
@ -150,18 +183,8 @@ class VexRiscv(Module, AutoCSR):
)
@staticmethod
def add_sources(platform, variant="std"):
verilog_variants = {
"std": "VexRiscv.v",
"std_debug": "VexRiscv_Debug.v",
"lite": "VexRiscv_Lite.v",
"lite_debug": "VexRiscv_LiteDebug.v",
"min": "VexRiscv_Min.v",
"min_debug": "VexRiscv_MinDebug.v",
"full": "VexRiscv_Full.v",
"full_debug": "VexRiscv_FullDebug.v",
}
cpu_filename = verilog_variants[variant]
def add_sources(platform, variant="standard"):
cpu_filename = CPU_VARIANTS[variant]
vdir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "verilog")
platform.add_source(os.path.join(vdir, cpu_filename))

View file

@ -1,3 +1,4 @@
#!/usr/bin/env python3
import os
import struct
import inspect
@ -23,6 +24,42 @@ __all__ = [
]
CPU_VARIANTS = {
# "official name": ["alias 1", "alias 2"],
"minimal" : ["min",],
"lite" : ["light", "zephyr", "nuttx"],
"standard": [None, "std"],
"full": [],
"linux" : [],
}
CPU_VARIANTS_EXTENSIONS = ["debug"]
class InvalidCPUVariantError(ValueError):
def __init__(self, variant):
msg = """\
Invalid cpu_variant value: {}
Possible Values:
""".format(variant)
for k, v in CPU_VARIANTS.items():
msg += " - {} (aliases: {})\n".format(k, ", ".join(str(s) for s in v))
ValueError.__init__(self, msg)
class InvalidCPUExtensionError(ValueError):
def __init__(self, variant):
msg = """\
Invalid extension in cpu_variant value: {}
Possible Values:
""".format(variant)
for e in CPU_VARIANTS_EXTENSIONS:
msg += " - {}\n".format(e)
ValueError.__init__(self, msg)
def version(with_time=True):
import datetime
import time
@ -77,6 +114,7 @@ def get_mem_data(filename, endianness="big", mem_size=None):
i += 1
return data
class ReadOnlyDict(dict):
def __readonly__(self, *args, **kwargs):
raise RuntimeError("Cannot modify ReadOnlyDict")
@ -162,7 +200,27 @@ class SoCCore(Module):
if cpu_type == "None":
cpu_type = None
self.cpu_type = cpu_type
self.cpu_variant = cpu_variant
# Support the old style which used underscore for separator
if cpu_variant is None:
cpu_variant = "standard"
cpu_variant = cpu_variant.replace('_', '+')
# Check for valid CPU variants.
cpu_variant_processor, *cpu_variant_ext = cpu_variant.split('+')
for key, values in CPU_VARIANTS.items():
if cpu_variant_processor not in [key,]+values:
continue
self.cpu_variant = key
break
else:
raise InvalidCPUVariantError(cpu_variant)
# Check for valid CPU extensions.
for ext in sorted(cpu_variant_ext):
if ext not in CPU_VARIANTS_EXTENSIONS:
raise InvalidCPUExtensionError(cpu_variant)
self.cpu_variant += "+"+ext
if integrated_rom_size:
cpu_reset_address = self.mem_map["rom"]
self.cpu_reset_address = cpu_reset_address

View file

@ -1,3 +1,4 @@
import subprocess
import unittest
import os
@ -6,6 +7,9 @@ from migen import *
from litex.soc.integration.builder import *
RUNNING_ON_TRAVIS = (os.getenv('TRAVIS', 'false').lower() == 'true')
def build_test(socs):
errors = 0
for soc in socs:
@ -98,8 +102,54 @@ class TestTargets(unittest.TestCase):
platforms += ["avalanche"] # PolarFire
for p in platforms:
os.system("litex/boards/targets/simple.py litex.boards.platforms." + p +
" --cpu-type=vexriscv " +
" --no-compile-software " +
" --no-compile-gateware " +
" --uart-stub=True")
with self.subTest(platform=p):
cmd = """\
litex/boards/targets/simple.py litex.boards.platforms.{p} \
--cpu-type=vexriscv \
--no-compile-software \
--no-compile-gateware \
--uart-stub=True \
""".format(p=p)
subprocess.check_call(cmd, shell=True)
def run_variants(self, cpu, variants):
for v in variants:
with self.subTest(cpu=cpu, variant=v):
self.run_variant(cpu, v)
def run_variant(self, cpu, variant):
cmd = """\
litex/boards/targets/simple.py litex.boards.platforms.arty \
--cpu-type={c} \
--cpu-variant={v} \
--no-compile-software \
--no-compile-gateware \
--uart-stub=True \
""".format(c=cpu, v=variant)
subprocess.check_output(cmd, shell=True)
# Build some variants for the arty platform to make sure they work.
def test_variants_riscv(self):
cpu_variants = {
'picorv32': ('standard', 'minimal'),
'vexriscv': ('standard', 'minimal', 'lite', 'lite+debug', 'full+debug'),
'minerva': ('standard',),
}
for cpu, variants in cpu_variants.items():
self.run_variants(cpu, variants)
def test_bad_variants(self):
with self.assertRaises(subprocess.CalledProcessError):
self.run_variant('vexriscv', 'bad')
def test_bad_variant_extension(self):
with self.assertRaises(subprocess.CalledProcessError):
self.run_variant('vexriscv', 'standard+bad')
@unittest.skipIf(RUNNING_ON_TRAVIS, "No lm32 toolchain on Travis-CI")
def test_variants_lm32(self):
self.run_variants('lm32', ('standard', 'minimal', 'lite'))
@unittest.skipIf(RUNNING_ON_TRAVIS, "No or1k toolchain on Travis-CI")
def test_variants_or1k(self):
self.run_variants('or1k', ('standard', 'linux'))