This block is listed in the following Xilinx® Blockset libraries: Control Logic, Math, and Index.
The Xilinx MCode block is a container for executing a user-supplied MATLAB function within Simulink. A parameter on the block specifies the M-function name. The block executes the M-code to calculate block outputs during a Simulink simulation. The same code is translated in a straightforward way into equivalent behavioral VHDL/Verilog when hardware is generated.
The block's Simulink® interface is derived from the MATLAB function signature, and from block mask parameters. There is one input port for each parameter to the function, and one output port for each value the function returns. Port names and ordering correspond to the names and ordering of parameters and return values.
The MCode block supports a limited subset of the MATLAB language that is useful for implementing arithmetic functions, finite state machines, and control logic.
The MCode block has the following three primary coding guidelines that must be followed:
- All block inputs and outputs must be of Xilinx fixed-point type.
- The block must have at least one output port.
- The code for the block must exist on the MATLAB path or in the same directory as the directory as the model that uses the block.
The example described below consists of a function xlmax which returns the maximum of its inputs. The second
illustrates how to do simple arithmetic. The third shows how to build a finite state
machine.
Configuring an MCode Block
The MATLAB Function parameter of an MCode block specifies the name of the block's M- code function. This function must exist in one of the three locations at the time this parameter is set. The three possible locations are:
- The directory where the model file is located.
- A subdirectory of the model directory named private.
- A directory in the MATLAB path.
The block icon displays the name of the M-function. To illustrate these ideas, consider
the file xlmax.m containing function xlmax:
function z = xlmax(x, y)
if x > y
z = x;
else
z = y;
end
An MCode block based on the function xlmax will have input ports
x and y and output port z.
The following figure shows how to set up an MCode block to use function
xlmax.
Once the model is compiled, the xlmax MCode block will appear like the block illustrated below.
MATLAB Language Support
The MCode block supports the following MATLAB language constructs:
- Assignment statements
- Simple and compound
if/else/elseifend statements -
switchstatements - Arithmetic expressions involving only addition and subtraction
- Addition
- Subtraction
- Multiplication
- Division by a power of two
- Relational operators:
< Less than <= Less than or equal to > Greater than >= Greater than or equal to == Equal to ~= Not equal to - Logical operators:
&And |Or ~Not
The MCode block supports the following MATLAB functions.
- Type conversion. The only supported data type is
xfix, the Xilinx fixed-point type. Thexfix()type conversion function is used to convert to this type. The conversion is done implicitly for integers but must be done explicitly for floating point constants. All values must be scalar; arrays are not supported. - Functions that return
xfixproperties:xl_nbits()Returns number of bits xl_binpt()Returns binary point position xl_arith()Returns arithmetic type - Bit-wise logical functions:
xl_and()Bit-wise and xl_or()Bit-wise or xl_xor()Bit-wise xor xl_not()Bit-wise not - Shift functions:
xl_lsh()andxl_rsh() - Slice function:
xl_slice() - Concatenate function:
xl_concat() - Reinterpret function:
xl_force() - Internal state variables:
xl_state() -
MATLAB Functions:
disp()Displays variable values error()Displays message and abort function isnan()Tests whether a number is NaN NaN()Returns Not-a-Number num2str()Converts a number to string ones(1,N)Returns 1-by-N vector of ones pi()Returns pi zeros(1,N)Returns 1-by-N vector of zeros
- Data Types
-
There are three kinds of
xfixdata types: unsigned fixed-point (xlUnsigned), signed fixed-point(xlSigned), and boolean (xlBoolean). Arithmetic operations on these data types produce signed and unsigned fixed-point values. Relational operators produce a boolean result. Relational operands can be anyxfixtype, provided the mixture of types makes sense. Boolean variables can be compared to boolean variables, but not to fixed-point numbers; boolean variables are incompatible with arithmetic operators. Logical operators can only be applied to boolean variables. Every operation is performed in full precision, for example, with the minimum precision needed to guarantee that no information is lost.Literal Constants
Integer, floating-point, and boolean literals are supported. Integer literals are automatically converted to
xfixvalues of appropriate width having a binary point position at zero. Floating-point literals must be converted to thexfixtype explicitly with thexfix()conversion function. The predefined MATLAB valuestrueandfalseare automatically converted to boolean literals.Assignment
The left-hand side of an assignment can only contain one variable. A variable can be assigned more than once.
Control Flow
The conditional expression of an
ifstatement must evaluate to a boolean. Switch statements can contain acaseclause and anotherwiseclause. The types of a switch selector and its cases must be compatible; thus, the selector can be boolean provided its cases are. All cases in aswitchmust be constant; equivalently, nocasecan depend on an input value.When the same variable is assigned in several branches of a control statement, the types being assigned must be compatible. For example,
if (u > v) x = a; else x = b; endis acceptable only if
aandbare both boolean or both arithmetic.
- Constant Expressions
-
An expression is constant provided its value does not depend on the value of any input argument. Thus, for example, the variable
cdefined bya = 1; b = a + 2; c = xfix({xlSigned, 10, 2}, b + 3.345);can be used in any context that demands a constant.
xfix() Conversion
The
xfix()conversion function converts adoubleto anxfix, or changes onexfixinto another having different characteristics. A call on the conversion function looks like the followingx = xfix(type_spec, value)Here
xis the variable that receives thexfix. type_spec is a cell array that specifies the type ofxfixto create, and value is the value being operated on. Thevaluecan be floating point orxfixtype. The type_spec cell array is defined using curly braces in the usual MATLAB method. For example,xfix({xlSigned, 20, 16, xlRound, xlWrap}, 3.1415926)returns an
xfixapproximation topi. The approximation is signed, occupies 20 bits (16 fractional), quantizes by rounding, and wraps on overflow.The type_spec consists of 1, 3, or 5 elements. Some elements can be omitted. When elements are omitted, default element settings are used. The elements specify the following properties (in the order presented):
data type,width,binary point position,quantization mode, andoverflow mode. Thedata typecan bexlBoolean,xlUnsigned, orxlSigned. When the type isxlBoolean, additional elements are not needed (and must not be supplied). For other types,widthandbinary point positionmust be supplied. Thequantizationandoverflow modes are optional, but when one is specified, the other must be as well. Three values are possible for quantization:xlTruncate,xlRound, andxlRoundBanker. The default isxlTruncate. Similarly, three values are possible for overflow:xlWrap,xlSaturate, andxlThrowOverflow. ForxlThrowOverflow, if an overflow occurs during simulation, an exception occurs.All values in a type_spec must be known at compilation time; equivalently, no type_spec value can depend on an input to the function.
The following is a more elaborate example of an
xfix()conversion:width = 10, binpt = 4; z = xfix({xlUnsigned, width, binpt}, x + y);This assignment to
xis the result of convertingx + yto an unsigned fixed-point number that is 10 bits wide with 4 fractional bits usingxlTruncatefor quantization andxlWrapfor overflow.If several
xfix()calls need the same type_spec value, you can assign the type_spec to a variable, then use the variable forxfix()calls. For example, the following is allowed:proto = {xlSigned, 10, 4}; x = xfix(proto, a); y = xfix(proto, b);
- xfix Properties: xl_arith, xl_nbits, and xl_binpt
-
Each
xfixnumber has three properties: the arithmetic type, the bit width, and the binary point position. The MCode blocks provide three functions to get these properties of a fixed- point number. The results of these functions are constants and are evaluated when Simulink compiles the model.Function
a = xl_arith(x)returns the arithmetic type of the input numberx. The return value is either1,2, or3forxlUnsigned,xlSigned, orxlBooleanrespectively.Function
n = xl_nbits(x)returns the width of the input numberx.Function
b = xl_binpt(x)returns the binary point position of the input numberx.
- Bit-wise Operators: xl_or, xl_and, xl_xor, and xl_not
-
The MCode block provides four built-in functions for bit-wise logical operations:
xl_or,xl_and,xl_xor, andxl_not.Function
xl_or,xl_and, andxl_xorperform bit-wise logical or, and, and xor operations respectively. Each function is in the form ofx = xl_op(a, b, ).Each function takes at least two fixed-point numbers and returns a fixed-point number. All the input arguments are aligned at the binary point position.
Function
xl_notperforms a bit-wise logical not operation. It is in the form ofx = xl_not(a). It only takes onexfixnumber as its input argument and returns a fixed- point number.The following are some examples of these function calls:
X = xl_and(a, b); Y = xl_or(a, b, c); Z = xl_xor(a, b, c, d); N = xl_not(x);
- Shift Operators: xl_rsh, and xl_lsh
-
Functions
xl_lshandxl_rshallow you to shift a sequence of bits of a fixed-point number. The function is in the form:x = xl_lsh(a, n)andx = xl_rsh(a, n)whereais axfixvalue andnis the number of bits to shift.Left or right shift the fixed-point number by
nnumber of bits. The right shift (xl_rsh) moves the fixed-point number toward the least significant bit. The left shift (xl_lsh) function moves the fixed-point number toward the most significant bit. Both shift functions are a full precision shift. No bits are discarded and the precision of the output is adjusted as needed to accommodate the shifted position of the binary point.Here are some examples:
% left shift a 5 bits a = xfix({xlSigned, 20, 16, xlRound, xlWrap}, 3.1415926) b = xl_rsh(a, 5);The output
bis of typexlSignedwith 21 bits and the binary point located at bit 21.
- Slice Function: xl_slice
-
Function
xl_sliceallows you to access a sequence of bits of a fixed-point number. The function is in the form:x = xl_slice(a, from_bit, to_bit).Each bit of a fixed-point number is consecutively indexed from zero for the LSB up to the MSB. For example, given an 8-bit wide number with binary point position at zero, the LSB is indexed as 0 and the MSB is indexed as 7. The block will throw an error if the
from_bitorto_bitarguments are out of the bit index range of the input number. The result of the function call is an unsigned fixed-point number with zero binary point position.Here are some examples:
% slice 7 bits from bit 10 to bit 4 b = xl_slice(a, 10, 4); % to get MSB c = xl_slice(a, xl_nbits(a)-1, xl_nbits(a)-1);
- Concatenate Function: xl_concat
-
Function
x = xl_concat(hi, mid, ..., low)concatenates two or more fixed-point numbers to form a single fixed-point number. The first input argument occupies the most significant bits, and the last input argument occupies the least significant bits. The output is an unsigned fixed-point number with binary point position at zero.
- Reinterpret Function: xl_force
-
Function
x = xl_force(a, arith, binpt)forces the output to a new type witharithas its new arithmetic type andbinptas its new binary point position. Thearithargument can be one ofxlUnsigned,xlSigned, orxlBoolean. Thebinptargument must be from 0 to the bit width inclusively. Otherwise, the block will throw an error.
- State Variables: xl_state
An MCode block can have internal state variables that hold their values from one
simulation step to the next. A state variable is declared with the MATLAB keyword persistent and must be initially assigned with an
xl_state function call.
The following code models a 4-bit accumulator:
function q = accum(din, rst)
init = 0;
persistent s, s = xl_state(init, {xlSigned, 4, 0});
q = s;
if rst
s = init;
else
s = s + din;
end
The state variable s is declared as persistent, and the first
assignment to s is the result of the xl_state
invocation. The xl_state function takes two arguments. The first is the
initial value and must be a constant. The second is the precision of the state variable.
It can be a type cell array as described in the xfix function call. It
can also be an xfix number. In the above code, if s =
xl_state(init, din), then state variable s will use din as
the precision. The xl_state function must be assigned to a persistent
variable.
The xl_state function behaves in the following way:
- In the first cycle of simulation, the
xl_statefunction initializes the state variable with the specified precision. - In the following cycles of simulation, the
xl_statefunction retrieves the state value left from the last clock cycle and assigns the value to the corresponding variable with the specified precision.
v = xl_state(init, precision) returns the value of a state variable.
The first input argument init is the initial value, the second argument
precision is the precision for this state variable. The argument
precision can be a cell arrary in the form of {type, nbits,
binpt} or {type, nbits, binpt, quantization,overflow}. The
precision argument can also be an xfix number.
v = xl_state(init, precision, maxlen) returns a vector object. The
vector is initialized with init and will have maxlen
for the maximum length it can be. The vector is initialized with init.
For example, v = xl_state(zeros(1, 8), prec, 8) creates a vector of 8
zeros, v = xl_state([], prec, 8) creates an empty vector with 8 as
maximum length, v = xl_state(0, prec, 8) creates a vector of one zero
as content and with 8 as the maximum length.
Conceptually, a vector state variable is a double ended queue. It has two ends, the front which is the element at address 0 and the back which is the element at length – 1.
Methods available for vector are:
val = v(idx);
|
Returns the value of element at address idx. |
v(idx) = val;
|
Assigns the element at address idx with val. |
f = v.front;
|
Returns the value of the front end. An error is thrown if the vector is empty. |
v.push_front(val);
|
Pushes val to the front and then increases the vector length by 1. An error is thrown if the vector is full. |
v.pop_front;
|
Pops one element from the front and decreases the vector length by 1. An error is thrown if the vector is empty. |
b = v.back;
|
Returns the value of the back end. An error is thrown if the vector is empty. |
v.push_back(val);
|
Pushes val to the back and the increases the vector length by 1. An error is thrown if the vector is full. |
v.pop_back;
|
Pops one element from the back and decreases the vector length by 1. An error is thrown if the vector is empty. |
v.push_front_pop_back(val);
|
Pushes val to the front and pops one element out from the back. It's a shift operation. The length of the vector is unchanged. The vector cannot be empty to perform this operation. |
full = v.full;
|
Returns true if the vector is full, otherwise,
false. |
empty = v.empty;
|
Returns true if the vector is empty, otherwise,
false. |
len = v.length;
|
Returns the number of elements in the vector. |
A method of a vector that queries a state variable is called a query
method. It has a return value. The following methods are query method:
v(idx), v.front, v.back,
v.full, v.empty, v.length,
v.maxlen. A method of a vector that changes a state variable is
called an update method. An update method does not return any
value. The following methods are update methods: v(idx) = val,
v.push_front(val), v.pop_front,
v.push_back(val), v.pop_back, and
v.push_front_pop_back(val). All query methods of a vector must be
invoked before any update method is invocation during any simulation cycle. An error is
thrown during model compilation if this rule is broken.
The MCode block can map a vector state variable into a vector of registers, a delay
line, an addressable shift register, a single port ROM, or a single port RAM based on
the usage of the state variable. The xl_state function can also be used
to convert a MATLAB 1-D array into a zero-indexed constant array. If
the MCode block cannot map a vector state variable into an FPGA, an error message is
issued during model netlist time. The following are examples of using vector state
variables.
Delay Line
The state variable in the following function is mapped into a delay line.
function q = delay(d, lat)
persistent r, r = xl_state(zeros(1, lat), d, lat);
q = r.back;
r.push_front_pop_back(d);
Line of Registers
The state variable in the following function is mapped into a line of registers.
function s = sum4(d)
persistent r, r = xl_state(zeros(1, 4), d);
S = r(0) + r(1) + r(2) + r(3);
r.push_front_pop_back(d);
Vector of Constants
The state variable in the following function is mapped into a vector of constants.
function s = myadd(a, b, c, d, nbits, binpt)
p = {xlSigned, nbits, binpt, xlRound, xlSaturate};
persistent coef, coef = xl_state([3, 7, 3.5, 6.7], p);
s = a*coef(0) + b*coef(1) + c*coef(2) + c*coef(3);
Addressable Shift Register
The state variable in the following function is mapped into an addressable shift register.
function q = addrsr(d, addr, en, depth)
persistent r, r = xl_state(zeros(1, depth), d);
q = r(addr);
if en
r.push_front_pop_back(d);
end
Single Port ROM
The state variable in the following function is mapped into a single port ROM.
function q = addrsr(contents, addr, arith, nbits, binpt)
proto = {arith, nbits, binpt};
persistent mem, mem = xl_state(contents, proto);
q = mem(addr);
- Single Port RAM
-
The state variable in the following function is mapped to a single port RAM in fabric (Distributed RAM).
function dout = ram(addr, we, din, depth, nbits, binpt) proto = {xlSigned, nbits, binpt}; persistent mem, mem = xl_state(zeros(1, depth), proto); dout = mem(addr); if we mem(addr) = din; endThe state variable in the following function is mapped to BlockRAM as a single port RAM.
function dout = ram(addr, we, din, depth, nbits, binpt,ram_enable) proto = {xlSigned, nbits, binpt}; persistent mem, mem = xl_state(zeros(1, depth), proto); persistent dout_temp, dout_temp = xl_state(0,proto); dout = dout_temp; dout_temp = mem(addr); if we mem(addr) = din; end
MATLAB Functions
- disp()
-
Displays the expression value. In order to see the printing on the MATLAB console, the option Enable printing with disp must be checked on the Advanced tab of the MCode block parameters dialog box. The argument can be a string, an
xfixnumber, or an MCode state variable. If the argument is anxfixnumber, it will print the type, binary value, and double precision value. For example, if variablexis assigned withxfix({xlSigned, 10, 7}, 2.75), thedisp(x)will print the following line:type: Fix_10_7, binary: 010.1100000, double: 2.75If the argument is a vector state variable,
disp()will print out the type, maximum length, current length, and the binary and double values of all the elements. For each simulation step, when Enable printing with disp is on and when a disp() function is invoked, a title line is printed for the corresponding block. The title line includes the block name, Simulink simulation time, and FPGA clock number.The following MCode function shows several examples of using the
disp()function.function x = testdisp(a, b) persistent dly, dly = xl_state(zeros(1, 8), a); persistent rom, rom = xl_state([3, 2, 1, 0], a); disp('Hello World!'); disp(['num2str(dly) is ', num2str(dly)]); disp('disp(dly) is '); disp(dly); disp('disp(rom) is '); disp(rom); a2 = dly.back; dly.push_front_pop_back(a); x = a + b; disp(['a = ', num2str(a), ', ', ... 'b = ', num2str(b), ', ', ... 'x = ', num2str(x)]); disp(num2str(true)); disp('disp(10) is'); disp(10); disp('disp(-10) is'); disp(-10); disp('disp(a) is '); disp(a); disp('disp(a == b)'); disp(a==b);The following lines are the result for the first simulation step.
xlmcode_testdisp/MCode (Simulink time: 0.000000, FPGA clock: 0) Hello World! num2str(dly) is [0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000] disp(dly) is type: Fix_11_7, maxlen: 8, length: 8, 0: binary 0000.0000000, double 0.000000, 1: binary 0000.0000000, double 0.000000, 2: binary 0000.0000000, double 0.000000, 3: binary 0000.0000000, double 0.000000, 4: binary 0000.0000000, double 0.000000, 5: binary 0000.0000000, double 0.000000, 6: binary 0000.0000000, double 0.000000, 7: binary 0000.0000000, double 0.000000, disp(rom) is type: Fix_11_7, maxlen: 4, length: 4, 0: binary 0011.0000000, double 3.0, 1: binary 0010.0000000, double 2.0, 2: binary 0001.0000000, double 1.0, 3: binary 0000.0000000, double 0.0, a = 0.000000, b = 0.000000, x = 0.000000 1 disp(10) is type: UFix_4_0, binary: 1010, double: 10.0 disp(-10) is type: Fix_5_0, binary: 10110, double: -10.0 disp(a) is type: Fix_11_7, binary: 0000.0000000, double: 0.000000 disp(a == b) type: Bool, binary: 1, double: 1 - error()
-
Displays message and abort function. See MATLAB help on this function for more detailed information. Message formatting is not supported by the MCode block. For example:
if latency <=0 error('latency must be a positive'); end - isnan()
-
Returns true for Not-a-Number.
isnan(X)returns true whenXis Not-a-Number.Xmust be a scalar value of double or Xilinx fixed-point number. This function is not supported for vectors or matrices. For example:if isnan(incr) & incr == 1 cnt = cnt + 1; end - NaN()
-
The
NaN()function generates an IEEE arithmetic representation for Not-a-Number. A NaN is obtained as a result of mathematically undefined operations like 0.0/0.0 and inf-inf. NaN(1,N) generates a 1-by-N vector of NaN values. Here are examples of using NaN.if x < 0 z = NaN; else z = x + y; end - num2Str()
-
Converts a number to a string.
num2str(X)converts theXinto a string.Xcan be a scalar value of double, a Xilinx fixed-point number, or a vector state variable. The default number of digits is based on the magnitude of the elements ofX. Here's an example ofnum2str:if opcode <=0 | opcode >= 10 error(['opcode is out of range: ', num2str(opcode)]); end - ones()
-
The
ones()function generates a specified number of one values.ones(1,N)generates a 1-by-N vector of ones.ones(M,N)whereMmust be 1. It's usually used withxl_state()function call. For example, the following line creates a 1-by-4 vector state variable initialized to [1, 1, 1, 1].persitent m, m = xl_state(ones(1, 4), proto) - zeros()
-
The
zeros()function generates a specified number of zero values.zeros(1,N)generates a 1-by-N vector of zeros.zero(M,N)whereMmust be 1. It's usually used withxl_state()function call. For example, the following line creates a 1-by-4 vector state variable initialized to [0, 0, 0, 0].persitent m, m = xl_state(zeros(1, 4), proto)
- FOR Loop
-
FORstatement is fully unrolled. The following function sumsnsamples.function q = sum(din, n) persistent regs, regs = xl_state(zeros(1, 4), din); q = reg(0); for i = 1:n-1 q = q + reg(i); end regs.push_front_pop_back(din);The following function does a bit reverse.
function q = bitreverse(d) q = xl_slice(d, 0, 0); for i = 1:xl_nbits(d)-1 q = xl_concat(q, xl_slice(d, i, i)); end
- Variable Availability
-
MATLAB code is sequential (for example, statements are executed in order). The MCode block requires that every possible execution path assigns a value to a variable before it is used (except as a left-hand side of an assignment). When this is the case, we say the variable is available for use. The MCode block will throw an error if its M-code function accesses unavailable variables.
Consider the following M-code:
function [x, y, z] = test1(a, b) x = a; if a>b x = a + b; y = a; end switch a case 0 z = a + b; case 1 z = a - b; endHere
a,b, andxare available, butyandzare not. Variableyis not available because the if statement has no else, and variablezis not available because the switch statement has no otherwise part.DEBUG MCode
There are two ways to debug your MCode. One is to insert
disp()functions in your code and enable printing; the other is to use the MATLAB debugger. For usage of the disp() function, see the disp() section in this topic.If you want to use the MATLAB debugger, you need to check the Enable MATLAB debugging option on the Advanced tab of the MCode block parameters dialog box. Then you can open your MATLAB function with the MATLAB editor, set break points, and debug your M-function. Just be aware that every time you modify your script, you need to execute a
clear functionscommand in the MATLAB console.To start debugging your M-function, you need to first check the Enable MATLAB debugging check box on the Advanced tab of the MCode block parameters dialog, then click the OK or Apply button.
Figure 8. Enable MATLAB DebuggingNow you can edit the M-file with the MATLAB editor and set break points as needed.
Figure 9. Set Break PointsDuring the Simulink simulation, the MATLAB debugger will stop at the break points you set when the break points are reached.
Figure 10. Stopping at Break PointWhen debugging, you can also examine the values of the variables by typing the variable names in the MATLAB console.
Figure 11. Examining Variable
There is one special case to consider when the function for an MCode block is executed from the MATLAB debugger. A
switch/caseexpression inside an MCode block must be typexfix, however, executing aswitch/caseexpression from the MATLAB console requires that the expression be adoubleorchar. To facilitate execution in the MATLAB console, a call todouble()must be added. For example, consider the following:switch i case 0 x = 1 case 1 x = 2 endwhere
iis typexfix. To run from the console this code must changed toswitch double(i) case 0 x = 1 case 1 x = 2 endThe
double()function call only has an effect when the M code is run from the console. The MCode block ignores thedouble()call.
- Passing Parameters
-
It is possible to use the same M-function in different MCode blocks, passing different parameters to the M-function so that each block can behave differently. This is achieved by binding input arguments to some values. To bind the input arguments, select the Interface tab on the block GUI. After you bind those arguments to some values, these M-function arguments will not be shown as input ports of the MCode block.
Consider for example, the following M-function:
function dout = xl_sconvert(din, nbits, binpt) proto = {xlSigned, nbits, binpt}; dout = xfix(proto, din);The following figures shows how the bindings are set for the din input of two separate
xl_sconvertblocks.Figure 12. din Bindings, Example 1
Figure 13. din Bindings, Example 2
The following figure shows the block diagram after the model is compiled.
Figure 14. Block Diagram
The parameters can only be of type double or they can be logical numbers.
- Optional Input Ports
-
The parameter passing mechanism allows the MCode block to have optional input ports. Consider for example, the following M-function:
function s = xl_m_addsub(a, b, sub) if sub s = a - b; else s = a + b; endIf
subis set to befalse, the MCode block that uses this M-function will have two input portsaandband will perform full precision addition. If it is set to an empty cell array {}, the block will have three input portsa,b, andsuband will perform full precision addition or subtraction based on the value of input portsub.The following figure shows the block diagram of two blocks using the same
xl_m_addsubfunction, one having two input ports and one having three input ports.Figure 15. Two Blocks Using Same xl_m_addsub Function
- Constructing a State Machine
-
There are two ways to build a state machine using an MCode block. One way is to specify a stateless transition function using a MATLAB function and pair an MCode block with one or more state register blocks. Usually the MCode block drives a register with the value representing the next state, and the register feeds back the current state into the MCode block. For this to work, the precision of the state output from the MCode block must be static, that is, independent of any inputs to the block. Occasionally you might find you need to use
xfix()conversions to force static precision. The following code illustrates this:function nextstate = fsm1(currentstate, din) % some other code nextstate = currentstate; switch currentstate case 0, if din==1, nextstate = 1; end end % a xfix call should be used at the end nextstate = xfix({xlUnsigned, 2, 0}, nextstate);Another way is to use state variables. The above function can be re-written as follows:
function currentstate = fsm1(din) persistent state, state=xl_state(0,{xlUnsigned,2,0}); currentstate = state; switch double(state) case 0, if din==1; state = 1; end end
- Reset and Enable Signals for State Variables
-
The MCode block can automatically infer register reset and enable signals for state variables when conditional assignments to the variables contain two or fewer branches.
For example, the following M-code infers an enable signal for conditional assignment of persistent state variable
r1:function myFn = aFn(en, a) persistent r1, r1 = xl_state(0, {xlUnsigned, 2, 0}); myFn = r1; if en r1 = r1 + a else r1 = r1 endThere are two branches in the conditional assignment to persistent state variable
r1. A register is used to perform the conditional assignment. The input of the register is connected tor1 + a, the output of the register isr1. The register's enable signal is inferred; the enable signal is connected toen, whenenis asserted. Persistent state variabler1is assigned tor1 + awhenenevaluates tofalse, the enable signal on the register is de-asserted resulting in the assignment ofr1tor1.The following M-code will also infer an enable signal on the register used to perform the conditional assignment:
function myFn = aFn(en, a) persistent r1, r1 = xl_state(0, {xlUnsigned, 2, 0}); myFn = r1; if en r1 = r1 + a endAn enable is inferred instead of a reset because the conditional assignment of persistent state variable
r1is to a non-constant value,r1 + a.If there were three branches in the conditional assignment of persistent state variable
r1, the enable signal would not be inferred. The following M-code illustrates the case where there are three branches in the conditional assignment of persistent state variabler1and the enable signal is not inferred:function myFn = aFn(en, en2, a, b) persistent r1, r1 = xl_state(0, {xlUnsigned, 2, 0}); if en r1 = r1 + a elseif en2 r1 = r1 + b else r1 = r1 endThe reset signal can be inferred if a persistent state variable is conditionally assigned to a constant; the reset is synchronous. Consider the following M-code example which infers a reset signal for the assignment of persistent state variable
r1toinit, a constant, whenrstevaluates to true andr1 + 1otherwise:function myFn = aFn(rst) persistent r1, r1 = xl_state(0, {xlUnsigned, 4, 0}); myFn = r1; init = 7; if (rst) r1 = init else r1 = r1 + 1 endThe M-code example above which infers reset can also be written as:
function myFn = aFn(rst) persistent r1, r1 = xl_state(0, {xlUnsigned,4,0}); init = 1; myFn = r1; r1 = r1 +1 if (rst) r1 = init endIn both code examples above, the reset signal of the register containing persistent state variable
r1is assigned torst. Whenrstevaluates totrue, the register's reset input is asserted and the persistent state variable is assigned to constantinit. Whenrstevaluates tofalse, the register's reset input is de-asserted and persistent state variabler1is assigned tor1 + 1. Again, if the conditional assignment of a persistent state variable contains three or more branches, a reset signal is not inferred on the persistent state variable's register.It is possible to infer reset and enable signals on the register of a single persistent state variable. The following M-code example illustrates simultaneous inference of reset and enable signals for the persistent state variable
r1:function myFn = aFn(rst,en) persistent r1, r1 = xl_state(0, {xlUnsigned, 4, 0}); myFn = r1; init = 0; if rst r1 = init else if en r1 = r1 + 1 end endThe reset input for the register of persistent state variable
r1is connected torst; whenrstevaluates totrue, the register's reset input is asserted andr1is assigned toinit. The enable input of the register is connected toen; whenenevaluates totrue, the register's enable input is asserted andr1is assigned tor1 + 1. It is important to note that an inferred reset signal takes precedence over an inferred enable signal regardless of the order of the conditional assignment statements. Consider the second code example above; if bothrstandenevaluate totrue, persistent state variabler1would be assigned toinit.Inference of reset and enable signals also works for conditional assignment of persistent state variables using switch statements, provided the switch statements contain two or less branches.
The MCode block performs dead code elimination and constant propagation compiler optimizations when generating code for the FPGA. This can result in the inference of reset and/or enable signals in conditional assignment of persistent state variables, when one of the branches is never executed. For this to occur, the conditional must contain two branches that are executed after dead code is eliminated, and constant propagation is performed.
- Inferring Registers
-
Registers are inferred in hardware by using persistent variables, however, the right coding style must be used. Consider the two code segments in the following function:
function [out1, out2] = persistent_test02(in1, in2) persistent ff1, ff1 = xl_state(0, {xlUnsigned, 2, 0}); persistent ff2, ff2 = xl_state(0, {xlUnsigned, 2, 0}); %code segment 1 out1 = ff1; %these two statements infer a register for ff1 ff1 = in1; %code segment 2 ff2 = in2; %these two statements do NOT infer a register for ff2 out2 = ff2; endIn code segment 1, the value of persistent variable ff1 is assigned to out1. Since ff1 is persistent , it is assumed that its current value was assigned in the previous cycle. In the next statement, the value of in1 is assigned to ff1 so it can be saved for the next cycle. This infers a register for ff1.
In code segment 2, the value of in2 is first assigned to persistent variable ff2, then assigned to out2. These two statements can be completed in one cycle, so a register is not inferred. If you need to insert delay into combinational logic, refer to the next topic.
- Pipelining Combinational Logic
-
The generated FPGA bitstream for an MCode block might contain many levels of combinational logic and hence a large critical path delay. To allow a downstream logic synthesis tool to automatically pipeline the combinational logic, you can add delay blocks before the MCode block inputs or after the MCode block outputs. These delay blocks should have the parameter Implement using behavioral HDL set, which instructs the code generator to implement delay with synthesizable HDL. You can then instruct the downstream logic synthesis tool to implement register re-timing or register balancing. As an alternative approach, you can use the vector state variables to model delays.
- Shift Operations with Multiplication and Division
-
The MCode block can detect when a number is multiplied or divided by constants that are powers of two. If detected, the MCode block will perform a shift operation. For example, multiplying by 4 is equivalent to left shifting 2 bits and dividing by 8 is equivalent to right shifting 3 bits. A shift is implemented by adjusting the binary point, expanding the
xfixcontainer as needed. For example, aFix_8_4number multiplied by 4 will result in aFix_8_2number, and aFix_8_4number multiplied by 64 will result in aFix_10_0number.
- Using the xl_state Function with Rounding Mode
-
The
xl_statefunction call creates anxfixcontainer for the state variable. The container's precision is specified by the second argument passed to thexl_statefunction call. If precision usesxlRoundfor its rounding mode, hardware resources is added to accomplish the rounding. If rounding the initial value is all that is required, anxfixcall to round a constant does not require additional hardware resources. The rounded value can then be passed to thexl_statefunction. For example:init = xfix({xlSigned,8,5,xlRound,xlWrap}, 3.14159); persistent s, s = xl_state(init, {xlSigned, 8, 5});
Block Parameters
The block parameters dialog box can be invoked by double-clicking the block icon in a Simulink® model.
As described earlier in this topic, the MATLAB function parameter on an MCode block tells the name of the block's function, and the Interface tab specifies a list of constant inputs and their values.
Other parameters used by this block are explained in the topic Common Options in Block Parameter Dialog Boxes.