There must be a Control block and a Signals block at the top level
The synthesizable part of your design must be a subsystem or contained within a subsystem of the top level.
There must be a Device block at the hierarchical level of the synthesizable part of the design.
Test-bench stimulus data-types feeding into the synthesizable design will be propagated – so ensure they are correct. Switch on all of ‘Port Data Types’, ‘Signal Dimensions’ and ‘Wide Non-Scalar Lines’ from the Simulink model Format > Port/Signal Displays menu to have these annotated to your model so they are visible.
Don’t try to pipeline the system yourself – this is what the tool does for you using its internal timing models and integer linear programming. Only add Sample Delays where they are part of the algorithm; that it where your algorithm explicitly requires you to think about combining data samples from different clock cycles. This can include feedback loops. If your design is not meeting timing, you may want to consider using the Clock Margin parameter on the top level Signals block, or on a LocalThreshold block.
Primitive subsystems must contain a SynthesisInfo block with style set to Scheduled, and the boundaries delimitied with ChannelIn/Out or GPIn/Out blocks.
All subsystem inputs with associated ‘Valid’ and ‘Channel’ signals that are to be scheduled together should be routed through the same ChannelIn blocks immediately following the subsystem inputs. Any other subsystem inputs should be routed through GPIn blocks.
All subsystem outputs with associated ‘Valid’ and ‘Channel’ signals that are to be scheduled together should be routed through the same ChannelOut blocks immediately before the subsystem outputs. Any other subsystem outputs should be routed through GPOut blocks.
Use Convert blocks to change data type preserving real-world value.
Use Set Via Dialog options to change data type preserving bit-pattern (with no bits added or removed), or to fix a data type.
Use Reinterpret Cast to change data type preserving bit-pattern (with no bits added or removed); for example if converting a ‘uint32’ to ‘single’
The valid signal is a scalar boolean or ufix(1).
The channel signal is a scalar uint(8).
Only a limited number of primitive blocks can be used outside of Primitive Subsystems but still within the synthesizable system. These are the blocks that amount to just signal routing or are explicit about their delay: SampleDelay, Constant, BitCombine, BitExtract, BitReverse, Not, Or, Xor, And, Nand, Nor, Xnor. Other primitive blocks should only be used within scheduled primitive subsystems or outside of the synthesizable part of the design.
Turn on ‘Create Automatic Testbenches’ and ‘Coverage in Testbenches’ on the Control block to use. Note that stimulus capture for test-benches is done on the inputs and outputs of ModelIP blocks and by ChannelIn, ChannelOut, GPIn and GPOut blocks.
Run and individual subsystem / ModelIP block testbench with
run_modelsim_atb(<path to subsystem> or <’gcb’ if currently selected>)
Run the single device-level testbench from the Run ModelSim block.
Look at generated resource summaries. After simulation, right click and select ‘Help’ – on the ModelIP block for ModelIP blocks, on the SynthesisInfo block for primitive subsystems, or on the Control block for a top level design summary.
Use the Run All Testbenches block to control test-benches – and to access the override feature, where ModelSim results can be automatically imported back in the Matlab and a custom Matlab function used to verify and provide the pass/fail criteria. (See appendix).
Simulink Design settings
Set Simulation > Configuration Parameters > Solver Options to “Fixed-step” / “discrete (no continuous states)”, unless folding, or you have multiple clocks in your test-bench in which case set to “Variable-step” / “discrete (no continuous states)”. This gives faster simulation than continuous solvers and also correct results round loops.
Tick all options on Format > Port/Signal Displays, except “Storage Class”. It is then clear which signals are complex, which are vectors, and the data types.
Hide the names of unimportant blocks to de-clutter your design using Format > hide name
If using From and Goto blocks, color linked blocks the same by selecting all linked Froms & Gotos (hold down shift while clicking on each block), then right click > background color. This makes tracing the connectivity easier to see.
Annotate your designs. Just double click anywhere on the background and start typing. Use Simulink Documentation blocks to link to external documentation.
Matlab Window > File & Folder comparisons is a great way to see what has changed between versions of your design.
Top Level Design
Use workspace variables to set parameters you may want to vary; including clock rates, sample rates, bit-widths, channels, etc.
Set workspace variables in initialization scripts. It is suggested that these are executed on the model’s PreLoadFnc and InitFcn callbacks, such that the design opens with parameters set, and any changes will be reflected in the next simulation, without having to explicitly run the script or open & close the model.
Call your main initialization script for the model ‘setup_<model name>’, and – as a shortcut to editing it – include the Edit Params block in the top level of your design. (This can be found by right-clicking on the Base Blocks library in the Simulink Library Browser and Selecting ‘Open Library’.)
Build a test-bench that is parameterizable – i.e. will vary correctly with system parameters such as Sample Rate, Clock Rate, and number of channels. The Channelizer block in Beta Utilities Library may be useful for this.
Use the model’s StopFnc call back to run any analysis scripts automatically
Build systems that make use of the valid and channel signals for control and synchronization; not latency matching. For example by capturing valid output in FIFOs to manage data-flow.
Build up and use your own libraries of reusable components. You can even use the “Configurable Subsystem block” in libraries to provide a single link from which you can select library implementations in place. (See “Configurable Subsystem block” in the Simulink help).
Keep block and subsystem names short, but descriptive. Avoid names with special characters, slashes or beginning with numbers.
Use LocalThreshold blocks, in conjunction with the top level thresholds, for localized trade-offs or pipelining effort tweaks if necessary.
Make use of vectors to build parameterizable designs – that don’t need redrawing when parameters such as number of channels changes.
Ensure there is sufficient Sample Delays around loops to allow for pipelining to the desired Fmax.
Data-type, complexity and vector width propagation is done by Simulink. Sometimes this is not successfully resolved round loops, particularly multiple nested loops. If unsuccessful, look for where data-types are not annotated. You may have to explicitly set data types. Else Simulink provides a library of functions to help in such situations, which duplicate data types etc. This is fixpt_dtprop (type ‘open fixpt_dtprop’ from the Matlab command prompt to open). The ‘Data Type Prop Duplicate’ block is used in the Control library latches, for example. These are guides to Simulink on data-type propagation, and do not produce hardware.
If routing within a Primitive Subsystem is getting complex, you might consider using Simulink From / Goto blocks to replace connections. Make sure that the Tag Visibility on the Goto blocks is Global if crossing subsystems within a primitive subsystems. You can color code blocks too (right click > background color) to make connections more obvious.
Don’t try to pipeline the design yourself – this is what the tool does for you using mathematical linear programming techniques. If you need more pipelining, use a positive Clock Margin (see Signals block).
Don’t try to synchronize the output of different parallel subsystems using explicit delays. Use FIFOs, as this will give a more device-portable, fmax target independent design.
Break designs up hierarchically to make your design understandable. However, keep consecutive primitive subsystems together within single ChannelIn/Out blocks, as this gives greater scope for scheduling and pipelining optimizations.
If you think you need complex control with complex feedback or cycle-counting from the data path, think again. Look at the Mandelbrot design and understand what it is doing. It creates command instructions which are placed in a FIFO. The instructions are consumed by the data-path as fast as it can run. The result is a design that runs as fast as it can and is portable between device families, and which reduces the complexity of the control logic.
Remember that output is only guaranteed to match hardware when valid is high.
run_modelsim_atb displays the command it executes. This command can be cut and paste into an open ModelSim UI open at the same directory and run manually. Using this you can analyze the behavior of particular subsystems in detail, and can force simulation to continue past errors if necessary.
If using FIFOs within multiple feedback loops, it is possible that while the data-throughput and frequency of invalid cycles is the same, their distribution over a frame of data many vary (due to the final distribution of delays around the loop). If a mismatch is found, it is therefore worth stepping past errors using the above process to check whether this is the case.
Floating point simulation is compared to within a tolerance. Differences are likely to be in the few least significant bits only, but could potentially be higher if the function you are implementing is ill-conditioned. Larger relative differences can also arise in complex multiplication with large complex numbers that lie close to the real or imaginary axis
Use the Run All Testbenches block to control test-benches – and to access the override feature, where ModelSim results can be automatically imported back in the Matlab and a custom Matlab function used to verify and provide the pass/fail criteria. (See appendix of DSP_Builder_Advanced_Blockset_-_Flow_Control,_Design_Style_and_Floating_Point).