[Oberon] Oberon FPGA hardware point of view

Jörg joerg.straube at iaeth.ch
Wed Aug 8 15:58:29 CEST 2018


Chris

With your file organization and Walter's syntax idea you could write something like this:

IMPORT MCU, SYSTEM;

PROCEDURE PutCh*(ch: CHAR);
    CONST txEmpty = 5;
    VAR
        lineStatus: SET AT MCU.ULSR;
        tx: CHAR AT MCU.UTHR;
    BEGIN
        REPEAT UNTIL txEmpty IN lineStatus;
        tx := ch
    END PutCh;

br
Jörg

Am 08.08.18, 15:33 schrieb "Oberon im Auftrag von Chris Burrows" <oberon-bounces at lists.inf.ethz.ch im Auftrag von chris at cfbsoftware.com>:

    > -----Original Message-----
    > From: Oberon [mailto:oberon-bounces at lists.inf.ethz.ch] On Behalf Of
    > Walter Gallegos
    > Sent: Wednesday, 8 August 2018 4:04 AM
    > To: oberon at lists.inf.ethz.ch; Walter Gallegos
    > Subject: [Oberon] Oberon FPGA hardware point of view
    > 
    > A FPGA hardware designer point of view;
    > 
    > In some projects (all my projects) the CPU executes software as
    > coprocessing; in parallel but outside the main data flow of hardware
    > DSP; hardware is faster and more efficient than software DSP.
    > 
    > On this scenery, the memory map could change from one project to
    > another project. So, hardware/software designers need certain degree
    > of freedom to access memory mapped areas.
    > 
    > I propose two modifications :
    > 
    > 1/ Add memory mapped variables
    > 
    > VAR [label] : [type] AT [address]
    > VAR [label] : ARRAY [size] OF [type] AT [address]
    > 
    
    On the surface this sounds OK but in reality the sheer number of addresses
    involved led us to use a different approach.
    
    Our technique follows Paul Reed's recent advice here i.e. separate all of
    the hardware specific details from everything else into the lowest level
    modules. This has worked out really well for us. It avoids the software
    maintenance nightmare of having to track down / modify hard-coded addresses
    and other hardware-specific details scattered throughout a system.
    
    With the Astrobe for ARM Cortex-M3, M4 and M7 Oberon systems we were faced
    with the prospect of having to maintain hundreds of memory-mapped addresses
    for similar, but different, memory-mapped peripherals for more than 60
    different types of microcontroller from two different manufacturers (NXP and
    STMicroelectronics). You might expect to have a couple of different sets of
    common definitions for each manufacturer but we ended up needing 11
    altogether. Not as bad as 60 perhaps but I'm convinced the designers could
    have done a lot better at eliminating inconsistencies. 
    
    The scheme we have used is this: There is a single module, called MCU.mod,
    for each family of microcontrollers e.g. LPC176x (NXP Cortex-M3), STM32F7
    (STM Cortex-M7) etc. There are eleven of these, all with the same name but
    stored in a folder named after the microcontroller family. 
    
    The *key* feature is: the *only* items contained in the MCU module are CONST
    declarations. 
    
    Each named constant represents a peripheral register address. Much of the
    time just the base address of each peripheral is different from one MCU to
    another so we take advantage of the fact that CONSTs can contain arithmetic
    expressions. We can then specify a single base address for each device and
    the other related registers are common offsets from that base. 
    
    e.g. 
    
    for UART0:
    
      U0Base* = 04000C000H;
      U0RBR* = U0Base+000H;
      U0THR* = U0Base+000H;
      U0DLL* = U0Base+000H;
      U0DLM* = U0Base+004H;
      ...
    
    For UART2:
    
      U2Base* = 040098000H;
      U2RBR* = U2Base+000H;
      U2THR* = U2Base+000H;
      U2DLL* = U2Base+000H;
      U2DLM* = U2Base+004H;
      ...
      ...
    
    RBR, THR, DLL, DLM etc. are the names used for each UART function exactly as
    used by NXP in their programming reference manual. In case you are
    wondering, yes - RBR, THR and DLL all map to the same absolute address.
    
    Now, just to be different, the corresponding base address for the LPC1347
    family is U0Base* = 040008000H. If that wasn't bad enough, sometimes the
    relative offset addresses are different as well! 
    
    There are more than 500 of these definitions in the LPC176x version of
    MCU.mod and there are still a number of peripherals that we have not yet
    included.
    
    Now that we have isolated what is *different* in MCU.mod, we can then
    implement a common hardware-interface module (e.g. Serial.mod) which uses
    these constant definitions, with their generic names, when implementing the
    (still hardware-specific) functions:
    
      IMPORT MCU, SYSTEM;
    
      PROCEDURE PutCh*(ch: CHAR);
      BEGIN
        REPEAT UNTIL SYSTEM.BIT(ULSR, 5);
        SYSTEM.PUT(UTHR, ch)
      END PutCh;
    
    
    The next level up in the module hierarchy is the familiar common
    *hardware-independent* 'Out' module. This works with all the different
    microcontrollers providing functions Out.Char, Out.String. It includes the
    statement:
    
      IMPORT Serial;
    
    And the functions just call PutCh in different ways.
    
    The mechanism that we use to specify which particular MCU.mod and Serial.mod
    files are actually used when we compile an application which targets a
    particular family of microcontrollers is to associate the application with a
    configuration file containing mcu-specific 'search paths'. An extract from
    the map file for an application called 'Info' shows the consequences:
    
    LPC1769:
    MCU             D:\AstrobeM3-v6.4\Lib\LPC1769\MCU.arm
    Out             D:\AstrobeM3-v6.4\Lib\General\Out.arm
    Serial          D:\AstrobeM3-v6.4\Lib\LPC1769\Serial.arm
    Info            D:\AstrobeM3-v6.4\Examples\General\Info.arm
    
    LPC1347:
    MCU             D:\AstrobeM3-v6.4\Lib\LPC1347\MCU.arm
    Out             D:\AstrobeM3-v6.4\Lib\General\Out.arm
    Serial          D:\AstrobeM3-v6.4\Lib\LPC1347\Serial.arm
    Info            D:\AstrobeM3\Release\Examples\General\Info.arm
    
    Most of the time the only functions we need to use with these CONST
    addresses are SYSTEM.PUT to write a value, SYSTEM.GET to read a value and
    SYSTEM.BIT to test the value of a single bit. 
    
    If multi-byte data needs to be efficiently read or written to an Oberon
    ARRAY or RECORD, SYSTEM.ADR, SYSTEM.COPY and SYSTEM.VAL (or ARRAY OF BYTE
    parameters) are the Oberon features that can be used. Once the conversion
    has been done from a byte-stream to an application-specific Oberon data
    structure at the hardware interface the Oberon data structure can be used
    naturally in the application from then on. It only needs to be converted
    back to a byte-stream when the hardware needs to be updated. 
    
    Regards,
    Chris Burrows
    CFB Software
    http://www.astrobe.com
    
    
    --
    Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems
    https://lists.inf.ethz.ch/mailman/listinfo/oberon
    




More information about the Oberon mailing list