EVIA SPACE

Համակարգչային Համակարգեր –
Ճարտարագիտություն եւ Նախագծում

Category: Welcome Note

  • Programming in Rust in Linux – bare basics

    Here I want to present programming in Rust basics for Linux environment, by Linux I mean modern distro such as Ubuntu 24.04 and Kernel 6.8 at least.

    So first we need to understand how Rust works and how to build programs in Rust. Rust has several versions and we will discuss them here in details.

    Rust compiler and build tool is dependent on C/C++ compiler. so we need to install gcc or llvm first, so for that we need to do the following:

    For Ubuntu/Debian: sudo apt update && sudo apt install build-essential

    For Fedora: sudo dnf install gcc

    For Arch Linux: sudo pacman -S base-devel

    For installing rust compiler run the following code in your terminal, and just follow simple instructions.

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

    Using Cargo
    When you start reading about Rust, you will soon meet Cargo, the standard tool used in the Rust ecosystem to build and run Rust application. Here we want to give a brief overview of what Cargo is and how it fits into the wider ecosystem of Rust application development.

    Installation

    Please follow the instructions on https://rustup.rs/

    This will give you the Cargo build tool, (cargo) and the Rust compiler (rustc). You will also get rustup, a command line utility that you use to install different compiler versions.

    After installing Rust, you should configure your PATH variable to be able to call rustc and cargo from command line. The PATH environment variable should contain standard binaries for Rust compiler and buidl tool – add this in PATH variable ~/.cargo/bin

    source "$HOME/.cargo/env" – for just local usage

    Edit ~/.bashrc and add the following to it

    export PATH="$HOME/.cargo/bin:$PATH" – for user’s global usage

    Then run the following commands to see versions.

    rustc --version
    cargo --version

    The Rust ecosystem

    The Rust ecosystem consists of a number of tools, of which the main ones are:

    • rustc: the Rust compiler that turns .rs files into binaries and other intermediate formats.
    • cargo: the Rust dependency manager and build tool. Cargo knows how to download dependencies, usually hosted on https://crates.io, and it will pass them to rustc when building your project. Cargo also comes with a built-in test runner which is used to execute unit test.
    • rustup: the Rust toolchain installer and updater. This tool is used to install and update rustc and cargo when new versions of Rust are released, rustup can also download documentation for the standard library. You can have multiple versions of Rust installed at once and rustup will let you switch between then as needed.

    Rust has editions: the current edition is Rust 2024. Previous editions were Rust 2015, Rust 2018, and Rust 2021.

    What is Rust?

    Rust is new programming language that had its 1.0 release in 2015:

    • Rust is statically compiled language in a similar role as C++
    • rustc uses LLVM as its backend
    • Rust supports many platforms and architectures – x86, ARM, WebAssembly, …
    • Linux, Mac, Windows, …
    • Rust is used for wide range of devices
    • firmware and boot loaders
    • smart displays
    • mobile phones
    • desktops
    • servers

    Rust is in the same area as C++.

    • High flexibility
    • High level of control
    • Can be scaled down to very constrained devices such as microcontrollers
    • Focuses on reliability and safety without sacrificing performance

    Benefits of Rust

    Some unique selling points of Rust:

    • Compile time memory safety – whole classes of memory bugs are prevented at compile time
    • No uninitialized variables
    • No double-frees
    • No NULL pointers
    • No forgotten locked mutexes
    • No data races between threads
    • No iterator invalidation
    • No undefined runtime behavior – what a Rust statement does is never left unspecified
    • Array access is bounds checked
    • Integer overflow is defined (panic or wrap-around)
    • Modern language features – as expressive and ergonomic as higher-level languages
    • Enums and pattern matching
    • Generics
    • No overhead FFI
    • Zero-cost abstractions
    • Great compiler errors
    • Built-in dependency manager
    • Built-in support for testing
    • Excellent Language Server Protocol support

    Types and Values

    Hello World

    hello_world.rs

    fn main() {
        println!("Hello World")
    }

    What you see:
    Functions are introduced with fn
    The main function is the entry point for the program
    Blocks are delimited by curly braces like in C and C++
    Statements end with semicolon ;
    println is a macro, indicated by the ! in the invocation
    Rust strings are UTF-8 encoded and can contain any Unicode character

    Rust is very much like other languages in the C/C++/Java tradition. It is imperative and it doesn’t try to reinvent things unless absolutely necessary. Rust is modern with full support for Unicode. Rust uses macros for situations where you want to have a variable number of arguments (no function overloading). println! is a macro because it needs to handle an arbitrary number of arguments based on the format string, which can’t be done with a regular function. Otherwise it can be treated like a regular function. Rust is multi-paradigm. For example, it has powerful object-oriented programming features, and, while it is not a functional language, it includes a range of functional concepts.

    Variables

    Rust provides type safety via static typing. Variables bindings are made with let:

    fn main() {
    let x: i32 = 10;
    println!("x: {x}");
    // x = 20;
    // println!("x: {x}");
    }

    Uncomment the x = 20 to demonstrate that variables are immutable by default. Add the mut keyword to allow changes.
    Warnings are enabled for this example, such as for unused variables or unnecessary mut. These are omitted in most examples to avoid distracting warnings. Try removing the mutation but leaving the mut keyword in place.
    The i32 here is the type of the variable. This must be known at compile time, but type inference (covered later) allows the programmer to omit it in many cases.

    Values

    Here are some basic built-in types, and the syntax for literal values of each type.

    TypesLiterals
    Signed integersi8, i16, i32, i64, i128, isize-10, 0, 1_000, 123_i64
    Unsigned integersu8, u16, u32, u64, u128, usize0, 123, 10_u16
    Floating point numbersf32, f643.14, -10.0e20, 2_f32
    Unicode scalar valueschar'a', 'A', '-'
    Booleanbooltrue, false

    The types have widths as follows.

    • iN, uN and fN are N bits wide
    • isize and usize are the width of a pointer
    • char is 32 bits wide
    • bool is 8 bits wide

    There are a few syntaxes that are not shown above:
    All underscores in numbers can be left out, the are for legibility only. So 1_000 can be written as 1000 (or 10_00), and 123_i64 can be written as 123i64.

    Arithmetic

    arithmetic.rs

    fn interproduct(a: i32, b: i32, c: i32) -> i32 {
        return a * b + b * c + c * a;
    }
    
    fn main() {
        println!("results: {}", interproduct(120, 100, 248));
    }

    This is the first time we have seen a function other than main, but the meaning should be clear: it takes three integers, and returns an integer. Functions will be covered in more detail later.

    Arithmetic is very similar to other languages, with similar precedence.

    What about integer overflow? In C and C++ overflow of signed integers are actually undefined, and might do unknown things at runtime. In Rust, it’s defined.

    Change the i32‘s to i16 to see an integer overflow, which panics (checked) in a debug build and wraps in a release build. There are other panic options, such as overflowing, saturating, and carrying. These are accessed with method syntax, e.g.
    (a * b).saturating_add(b * c).saturating_add(c * a).

    In fact, the compiler will detect oveflow of constant expressions, which is why the example requires a separate function.

    Type Inference

    Rust will look at how the variable is used to determine the type:

    inference.rs

    fn takes_u32(x: 32) {
    println!("u32: {x}");
    }

    fn takes_i8(y: i8) {
    println!("i8: {y}");
    }

    fn main() {
    let x = 10;
    let y = 20;

    takes_u32(x);
    takes_i8(y);
    }

    This example demonstrates how the Rust compiler infers types based on constraints given by variable declarations and usages.

    It is very important to emphasize that variables declared like this are not of some soft of dynamic “any type” that can hold any data. The machine code generated by such declaration is identical to the explicit declaration of a type. The compiler does the job for us and helps us write more concise code.

    When nothing constrains the type of an integer literal, Rust defaults to i32. This is sometimes appears as {integer} in error messages. Similarly, floating-point literals default to f64.

  • LED Blink Example in VHDL for Spartan 6 FPGA board

    Here I would like to present LED Blink Example for Spartan 6 XC6SLX9-TQG144 FPGA, that I have from 2012, may be its quiet oldie now, but I want to share all my experience here.

    My Spartan 6 FPGA Core Board

    Here is this board I have 2 programmable LED and one 7 segment lcd, we gonna program LED for blinking ~1sec using VHDL. I have 50MHz clock source onboard.

    LED_Blink.vhd

    -- LED_Blink.vhd for Blinking onboard LED0
    library IEEE;
    use IEEE_STD_LOGIC_1164.all;
    use IEEE.NUMERIC_STD.all;

    entity LED_Blink is
    port (
    CLK : in STD_LOGIC; -- board clock 50MHz
    LED : out STD_LOGIC; -- onboard LED0
    );
    end LED_Blink;

    architecture Behavioral of LED_Blink is
    signal counter : unsigned(24 downto 0) := (others => '0');
    signal blink : STD_LOGIC := '0';
    begin
    process(CLK)
    begin
    if rising_edge(CLK) then
    if counter = 25_000_000 -1 then
    blink <= not blink;
    counter <= (others => '0');
    else
    counter <= counter + 1;
    end if;
    end if;
    end process;

    LED <= blink;
    end Behavioral;

    pinout.ucf

    # CLOCK source
    NET "CLK" LOC = "P55";
    
    # LED0 pin
    NET "LED" LOC = "P127"; # LED0 pin

  • VHDL design flow using Terminal for Xilinx ISE 14.7 and Spartan 6 FPGA

    Here I would like to present the CLI or Terminal based flow of VHDL design and synthesis using Ubuntu 12.04 local network server and Xilinx ISE 14.7 building scripts. Assuming Xilinx ISE installed correctly and PATH env variable is configured by original shell script –
    source /opt/Xilinx/path_to_ise/settings64.sh

    Also make sure you have installed libusb-dev and all necessary drivers for USB Cable programmer to work fully fine!

    Build Generics
    Makefile

    SHELL := /bin/bash
    
    # Spartan 6 LX9, TQG144 Package, XC6SLX9-TQG144
    PART := xc6slx9-tqg144-3
    
    # Top-level module name
    TOP := top
    
    # Source and constraint files
    SRCS := $(TOP).vhd
    # TB_SRCS := top_tb.v -- use if needed to have
    UCF := pinout.ucf
    PRJ := $(TOP).prj
    XST_SCRIPT := $(TOP).xst
    
    # Output files
    NGC := $(TOP).ngc
    NGD := $(TOP).ngd
    MAP_NCD := $(TOP)_map.ncd
    PCF := $(TOP).pcf
    ROUTED_NCD := $(TOP)_routed.ncd
    BIT := $(TOP).bit
    BTMMAP := $(TOP).bmm
    
    .PHONY: all help synth translate map par bitgen prog sim prj xst clean distclean flash
    
    all: bitgen
    
    help:
        @echo "Available targets:"
        @echo " make all"        - build bitstream ($(BIT))"
        @echo " make synth.      - run XST synthesis"
        @echo " make translate   - run NGDBUILD"
        @echo " make map         - run MAP"
        @echo " make par         - run PAR"
        @echo " make bitgen      - generate bitstream"
        @echo " make sim         - run VHDL simulation"
        @echo " make prog        - open iMPACT in batch mode using impact.cmd"
        @echo " make clean.      - remove generated build files"
        @echo " make distclean   - remove generated files plus auto-creates scripts"
        @echo " make flash       - flash SPI W25Q64BV 8MByte SPI Flash to make bit file permanent"
        @echo ""
        @echo "Edit these variables if needed"
        @echo " TOP=$(TOP)"
        @echo " PART=$(PART)"
        @echo " SRCS=$(SRC)
        @echo " UCF=$(UCF)"
    
    $PRJ: $(SRCS)
             @rm -f $(PRJ)
             @for f in $(SRCS); do echo "vhdl work $$f" >> $(PRJ); done
    
    $(XST_SCIRPT): $(PRJ)
              @print '%s\n' \
                     "run" \
                     "-ifn $(PRJ)" \
                     "-ifmt mixed" \
                     "-top $(TOP)" \
                     "-ofn $(TOP)" \
                     "-ofmt NGC" \
                     "-p $(PART)" \
                     > $(XST_SCRIPT)
    
    synth: $(NGC)
    
    $(NGC): $(SRCS) $(PRJ) $(XST_SCRIPT)
            xst -ifn $(XST_SCRIPT)
    
    translate: $(NGD)
    
    $(NGD): $(NGC) $(UCF)
            ngdbuild -uc $(UCF) -p $(PART) $(NGC) $(NGD)
    
    map: $(MAP_NCD)
    
    $(MAP_NCD): $(NGD)
            map -p $(PART) -o $(MAP_NCD) $(NGD) $(PCF)
    
    par: $(ROUTED_NCD)
    
    $(ROUTED_NCD): $(MAP_NCD) $(PCF)
            par -w $(MAP_NCD) $(ROUTED_NCD) $(PCF)
    
    bitgen: $(BIT)
    
    $(BIT): $(ROUTED_NCD)
            bitgen -w $(ROUTED_NCD) $(BIT)
    
    sim:
            @echo "Simulation implementation not done. Please add your code here"
    
    prog: $(BIT)
            impact -batch impact.cmd
    
    flash:
            impact -batch flash.cmd
    
    clean:
            rm -f xst.log xst.xrpt
            rm -f ngdbuild.log map.log par.log bitgen.log
            rm -f *.b *.drc *.lso *.map *.mrp *.ncd *.ngc *.ngd *.ngr *.pad
            rm -f *.par *.pcf *.ptwx *.srp *.bgn *.bin *.bit *.cmd_log *.html *.xml
            rm -f *.xrpt *.xpi *.xwbt *.twx *.txt *_envsettings.html usage_statistics_webtalk.html
            rm -f simv a.out # for simulation files
    
    distclean: clean
            rm -f $(PRJ) $(XST_SCRIPT)

    top.prj

    vhdl work "top.vhd"
    # vhdl work other_modules.vhd
    # Add all your files

    top.xst

    run
    -ifn top.prj
    -ifmt mixed
    -top top
    -ofn top
    -ofmt NGC
    -p xc6slx9-tqg144-3

    imapct.cmd

    setMode -bscan
    setCable -p auto
    identify
    assignFile -p 1 -file top.bit
    program -p 1
    quit

    promgen.sh

    #!/bin/bash
    
    echo "Generating MCS PROM File for Board QSPI Flash - W25Q64BV Flash IC"
    
    promgen -w -p mcs -c FF top.mcs -s 8192 -u 0000 top.bit -spi

  • Hello world!

    My own website and projects