Best practices for model cleanup

In this blog I have written a lot about “mushroom” and “spaghetti” code; today I’m going to write about the best practices for updating and cleaning up those models

Should I update?

Before you start you should ask yourself three questions

  1. Beyond cleanup are there additional modifications needed to the model? (No)
  2. Is the model, as written, performing it’s intended function? (Yes)
  3. Do I have tests cases that cover the full range of behavior of the model? (Yes)

If you answered as indicated (no,yes,yes) then stop. Spend time on another part of your code that does not meet those criteria(1). Otherwise lets start…

Baselining the model

The first step in cleaning up code is baselining the model. This activity consists of N steps

  1. Back up the model’s current state: Ideally this is already handled by your version control software but….
  2. Generate baseline test vectors: To the degree possible create baseline tests, these could be auto-generated.
  3. Generate baseline metrics: Generate the baseline metrics for the model, ram / rom usage, execution time, model coverage…
  4. Create the “Difference Harness”: The difference harness compares the original model to the update model by passing in the initial test vectors and comparing the outputs.

What is different about today?

The next question to ask in your refactoring is “do I re-factor or do I redo”? Depending on the state of the model there are times when simply re-doing the model from scratch is the better choice. This is often the case when the model was created before requirements existed and, as a result, does not meet them; that would make for a very short article though so let us assume that you are refactoring. First figure out what needs and what should change. To do that ask the following questions.

  • Review the requirements: what parts of the requirements are met, which are incorrect and which are missing?
    • Prioritize missing and incorrect requirements
  • Is it possible to decompose the model into sub-components: In most cases, the answer is no, or yes but it is tangled. It wouldn’t be mushroom code if you could.
    • Create partitioning to enable step-based modifications
  • Identify global data and complex routing: Minimization of global data should be an objective of update, complex routing is an indication that the model is not “conceptually” well decomposed
    • Move sections of the model to minimize signal routing and use of global data
  • Identify the “problem” portions of the model: Which sections of the model most frequently has bugs?
    • Squash them.

Once you have asked these questions you understand your priorities in updating the model

Begin modification

First understand the intent of the section of the model, either through inspection or through review of the requirements . Once you understand what the intention is you can start to simplify and clarify.

  • Simplifying logical statements / state charts
    • Run tool such as Simulink Design Verifier to check for dead branches, trim or fix
    • Look for redundant logical checks (multiple transitions all using the same “root” condition check)
    • Look for redundant states (multiple states exist all with the same entry and exit conditions)
  • Mathematical equations
    • Did they create blocks to replicate built in blocks? (Tables, sine, transfer functions)
      • Replace them with built-in blocks
    • Are complex equations being modeled as Simulink blocks?
      • Replace them with a MATLAB function
  • Size (to big or to small)
  • Partitioning rationale

Footnotes

  1. With mushroom code it is highly unlikely that you have tests cases that cover the full range of behavior of the model; model (or code) coverage should not be confused with full behavioral coverage since it is possible to auto-generate tests cases that would cover the full model without every understanding what that coverage means
  2. One advantage of having this blog for 3+ years is I can mine back article for information. Hopefully you will as well. What I mine is yours, nuggets of MBD wisdom.

Know your history: The Stateflow block

The history junction block in Stateflow™ is a simple, powerful, and often misunderstood entity.  In this blog post, I will try to explain the use of the history junction, through a, hopefully, humorous example.

The Model

For this example, I have coded two versions of the same model, one with the history block and one without.  By doing so I can illustrate the effect of the block.

HistoryJunction

In this example, the first state is the “MortgageStatus”; it has three substates, start of loan, paying off loan and you own your home.  If all progresses normally you will be in the final state after 30 iterations of the chart.

However, there is a second top level state, “BankFraudOccured.”  For this model, I have this configured to be true every 10th cycle.  So let’s look at the results with and without the history junction.

The results

The function of the history junction is to allow users to return to the last active substate within a super state; without it, the user returns to the default substate.  If we look at the two graphs we will see the effect of history junction.

mortgageResults

In the left-hand graph, the instance with the History Junction, the effect of the “bank fraud event” does not impact the payoff of the mortgage.  However, in the right-hand graph, the bank fraud “resets” the mortgage to the “NEW” state and keeps the lender paying off indefinitely.   This can be clearly seen by looking at the Sequence Viewer.

sequenceMortgage.png

The red boxes her show where the model transitions to and from “BankFraud.”  With the history junction in place, you go right back to paying.  Without you follow the default junction to the starting substate.

Words of caution

So in this first example, we had a just a single state connected to our main state. Now let us take a look at a multi-state example

refi.png

In this example, the History Junction would resultmonopoly

in the state returning to “Paying Off” without going through the “Start of Loan” state.   The history junction applies to all transitions into and out of the parent state.   To correct for this the “Refinance State” needs to be to be “Start of Loan” state

refiRight.png

Final thoughts

The first iteration of MAAB style guidelines recommended against the use of the history junction.  With version 2.0 this recommendation was removed and it opened up new modeling patterns for our end users.  Use of the history junction enables multiple design patterns that are difficult to create without it.  Hopefully, this post sheds some light on its proper use.