Saturday 31 October 2015

TDD with VHDL

We've recently been doing some interesting prospective work on FPGAs with VHDL and I thought it would be cool to try and update www.cyber-dojo.org to support the VHDL language.

Cyber Dojo has been our in-house training tool of choice for a long time now. It essentially enables you to practise programming the TDD way in a web-based environment. Multiple developers can join the same dojo session and we all review together at the end.

The first challenge was setting up a development envrionment for Cyber Dojo, which essentially meant getting a "Ruby on Rails" Linux server up and running. I chose to do this from scratch rather than paying for an image, so I went for the Manjaro distribution for a few reasons :

1. It's Arch based, and Arch is relatively lightweight
2. If we were using raw Arch it would be a pain the ass
3. I've used Manjaro before and it was relatively painless

After some fiddling around, I decided to use the command line of Passenger to create a development HTTP server using our forked cyber dojo code. Passenger can also run through Apache but it looked like a lot more of a faff.

All credit to my team member Pablo Mansanet who then found GHDL here :
http://home.gna.org/ghdl/

... and created the relevant docker files + other changes to Cyber Dojo to support the VHDL language.

We tried it internally (twice).... ironed out a couple of problems (largely related to renaming files)... pull-requested to Jon Jagger.... and wham! VHDL is now supported by Cyber Dojo :)


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity hiker_testbench is
end hiker_testbench;

architecture test_fixture of hiker_testbench is
   signal meaning_of_life_test : std_logic_vector (7 downto 0);
begin
   UUT: entity work.hiker port map (meaning_of_life_test);

   process
   begin
       wait for 1 ns; -- Signal propagation
       assert (meaning_of_life_test = "00101010") -- 42
               report "Meaning of life value incorrect"
               severity failure;
      
       assert false report "End of test" severity note;
       wait;
   end process;
end test_fixture;


If you fancy a more appropriate challenge than the existing exercises, we implemented a "half adder" as a test exercise which didn't stretch any of our less VHDL-savvy developers too badly (and also provided a base for a full adder so the guys who have more experience could leap ahead).

https://en.wikipedia.org/wiki/Adder_(electronics)

Enjoy! :)

Saturday 21 February 2015

C++ BDD with Igloo/Bandit/Homebrew


It's been a while since I posted, so I thought it would be good to give an update.

We had some problems working with SpecFlow using C++, mainly around the "shim" layer we created to provide interopability.

The major issues were :-

1. Debugging ("the breakpoint game" trying to debug across layers)
2. C++ developers are not C# developers, and not all of them want to be
3. Management perception of reliance on C# as a risk
4. Fragile shim layer that doesn't allow the transport of all data types/classes

There were also a plethora of "niggles" which we mostly worked out, but overall no one was particularly happy with the solution.

Instead we decided to try "Igloo", a C++ BDD framework one of our developers found (we layer upgraded this to "Bandit"). It essentially provides macros to facilitate human readable specification tests :-
http://banditcpp.org/

We augmented this and developed Python scripts that allow us to translate a feature file something like this :-

 

   Feature: EatingApple

   Scenario: First bite


    Given an apple waiting to be eaten
    When teeth are sunk into it
    Then juice flies around it



Into this :-
Feature("EatingApple") Handler(EatingAppleSteps)
 
   GTestID(first_bite, EatingApple)
   Scenario("First bite")
      Given("an apple waiting to be eaten") StepPlay(GivenAnAppleWaitingToBeEaten)
      When("teeth are sunk into it") StepPlay(WhenTeethAreSunkIntoIt)
      Then("juice flies around it") StepPlay(ThenJuiceFliesAroundIt)
   EndScenario

At this stage we are now looking at directly executable C++ code. Cool, right? It uses something rather magical called a StepHandler class. As you can probably see, it manages the parsing of parameters in the human-readable feature file (I've left them out of the above example for simplicity) :-
class StepHandler
{
public:
   std::string GetCurrentLine();
public:
   void SetCurrentLine(std::string line);
   void Echo();
   void NotImplemented();
   int get_CurrentRow();
   void set_CurrentRow(int currentRow);
   void IncrementRow();
protected:
   std::vector<std::string> m_parameters;
   std::string m_currentLine;
   int m_notImplemented = 0;
   void ClearParameters();
   void ExtractParameters();
   void StripDelimiters(std::string& parameter);
   std::string GetStringParameter(int index);
};

The outputted C++ code's Handler() macro indicates which step handler the StepPlay() macros use to make their calls :-
class EatingAppleSteps : public StepHandler
{
public:
   EatingAppleSteps();
   ~EatingAppleSteps();
   void GivenAnAppleWaitingToBeEaten();
   void WhenTeethAreSunkIntoIt();
   void ThenJuiceFliesAroundIt();
};

In case you hadn't already realised, the constructor and destructor constitute the setup and tear down for each of the tests.

Beautiful, right? We now have a full C++ BDD system with many supporting scripts, fully integrated with google test, and are very happy programmers again.

Credit to James for his amazing work on this system and Seb Rose for the warnings which helped us avoid several dangerous pitfalls on the way - primarily the importance of synchronised feature/executable feature files. We generate an error in the build if they don't match. This was an excellent steer.

Feel free to contact me if you have any questions :)