# Getting Started
These code snippets are fully self-contained examples, each of which can be built in Moku Cloud Compile and achieves some basic task.
# More Examples
Many more examples are available on Gitlab (opens new window).
# On This Page
# The Basic Component Structure
All Moku Cloud Compile top-level blocks are architectures of CustomWrapper.
library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Numeric_Std.all;
architecture Behavioural of CustomWrapper is
begin
end architecture;
# Control Registers
The Custom Wrapper has Inputs, Outputs and Control Registers. The input and output routing is determined in the Multi-instrument Mode (MiM) configuration screen. They can be connected to ADCs and DACs respectively, but can also be attached to other slots in order to pre- or post-process instrument data.
The control registers have 32-bit values that can be changed through the MCC instrument screen on the Moku: application. Here we interpret 16 bits of each of the first two control registers as a (signed) DC voltage to output from the MCC instrument. For example, if the MiM configuration routes the MCC slot outputs to the DACs, this is a simple programmable DC supply.
library IEEE;
use IEEE.Numeric_Std.all;
architecture Behavioural of CustomWrapper is
begin
OutputA <= signed(Control1(15 downto 0));
OutputB <= signed(Control2(15 downto 0));
end architecture;
Control register bits can also be used to enable and disable features.
architecture Behavioural of CustomWrapper is
begin
OutputA <= InputA when Control1(0) = '1' else (others => '0');
OutputB <= InputB when Control1(1) = '1' else (others => '0');
end architecture;
# Some arithmetic
Basic arithmetic is available in the VHDL language, but note that this is purely combinatorial so can run in to timing errors.
-- A very simple example, simply add two inputs and route to an output.
-- This is purely combinatorial
architecture Behavioural of CustomWrapper is
begin
OutputA <= InputA + InputB;
OutputB <= InputA - InputB;
end architecture;
# Instantiate a DSP
For more complex arithmetic, it's common to explicitly instantiate a DSP block in the FPGA. The Moku.Support.ScaleOffset
entity conveniently packages a DSP block with all the settings configured to compute the common Z = X * Scale + Offset
operation, with the output properly clipped to prevent under/overflow.
library IEEE;
use IEEE.Numeric_Std.all;
library Moku;
use Moku.Support.ScaleOffset;
-- Instatiate a DSP block using the ScaleOffset wrapper
architecture Behavioural of CustomWrapper is
begin
-- Z = X * Scale + Offset
-- Offset is units of bits, scale by default runs from -1 to 1 across whatever signal width is given
-- Clips Z to min/max (prevents over/underflow)
-- Includes rounding
-- One Clock Cycle Delay
DSP: ScaleOffset
port map (
Clk => Clk,
Reset => Reset,
X => InputA,
Scale => signed(Control0(15 downto 0)),
Offset => signed(Control1(15 downto 0)),
Z => OutputA,
Valid => '1',
OutValid => open
);
-- If you want to change the range of the scale (e.g. multiply by more than 1), then set the
-- NORMAL_SHIFT generic. This increases the range of Scale by 2^N, so NORMAL_SHIFT=4 means that
-- the 16 bit scale here now covers the range -16 to 16.
DSP: ScaleOffset
generic map (
NORMAL_SHIFT => 4
)
port map (
Clk => Clk,
Reset => Reset,
X => InputB,
Scale => signed(Control2(15 downto 0)),
Offset => signed(Control3(15 downto 0)),
Z => OutputB,
Valid => '1',
OutValid => open
);
end architecture;
# Moku Library
The Moku Library contains many useful helper functions and components, like the ScaleOffset
block used above. For example, the clip
function clips a signal to a defined bit range, gracefully handling saturation.
library IEEE;
use IEEE.Numeric_Std.all;
library Moku;
use Moku.Support.clip;
architecture Behavioural of CustomWrapper is
begin
OutputA <= resize(clip(InputA, 8, 0), 16);
end architecture;