1. This forum section is a read-only archive which contains old newsgroup posts. If you wish to post a query, please do so in one of our main forum sections (here). This way you will get a faster, better response from the members on Motherboard Point.

Has someone tried to add interrupt support to Riscy Pygness?

Discussion in 'Embedded' started by wzab, May 21, 2011.

  1. wzab

    wzab Guest

    I just started to play with Riscy Pygness ( http://pygmy.utoh.org/riscy/cortex/
    ) on target part of LPCXpresso 1769 board. The system works quite good
    as interactive environment for experimenting with ARM CORTEX
    processor, and seems to be an efficient development platform.

    The only thing which seems to be lacking is interrupt support.
    Well, I can write the interrupt service routine in assembly and add it
    to the kernel, and it will be the most efficient solution. However for
    interactive experiments it would be much nicer to have possibility to
    implement ISR as a Forth word. Such functionality is available in the
    latest version of amforth for ATmega's (http://amforth.sf.net).

    Has anybody added this functionality to Riscy Pygness? Maybe someone
    could suggest the best way to implement such functionality? Is it
    possible to make use of multitasking functionalities provided
    by the kernel to service interrupts in a reasonable way?
    What are the traps, which may badly affect the interrupt servicing
    latency?
     
    wzab, May 21, 2011
    #1
    1. Advertisements

  2. I am not familiar with Riscy Pygness, but I can comment generally about
    interrupt handling.

    The problem with writing your interrupt handler in high-level Forth is
    that the interrupt must first instantiate a Forth environment: stacks,
    registers, whatever the implementation requires, and do so in such a way
    as to not affect whatever task was running when the interrupt occurred.
    This adds some inevitable overhead.

    IMO the faster approach is to write the interrupt handler itself in
    assembler, and make it do a bare minimum: only those things that
    absolutely have to be done *right now*, and let a high-level word do the
    less time-critical functions.

    For example, if you are doing some data acquisition, your application
    task can set up the operation and then go into a wait state. When the
    interrupt occurs, the ISR can read the value, store it somewhere, and
    set the task to wake up again to do whatever additional processing is
    necessary.

    Of course, I am viewing this from a multitasking perspective.

    Cheers,
    Elizabeth

    --
    ==================================================
    Elizabeth D. Rather (US & Canada) 800-55-FORTH
    FORTH Inc. +1 310.999.6784
    5959 West Century Blvd. Suite 700
    Los Angeles, CA 90045
    http://www.forth.com

    "Forth-based products and Services for real-time
    applications since 1973."
    ==================================================
     
    Elizabeth D Rather, May 21, 2011
    #2
    1. Advertisements

  3. wzab

    rickman Guest

    I can't say I follow your idea of what an interrupt handler in forth
    has to do. The stacks are already there. If an interrupt works on
    top of that there shouldn't be any issues other than possibly
    overflowing the stacks. If the interrupt routine needs storage
    between invocations that would be variable memory, no?

    That is one of the things I've never understood. Forth is a language
    based on a virtual machine. That virtual machine is perfectly capable
    of handling interrupts from what I can see. The only limitation would
    be if interrupts are allowed during the execution of an assembly
    language defined word, then that word needs to protect any section of
    code that should not be interrupted at the Forth level. Otherwise I
    don't see the issues.

    Rick
     
    rickman, May 22, 2011
    #3
  4. As I said, I'm viewing this from a multitasking perspective: when an
    interrupt occurs there's no telling which task is running and has its
    stacks instantiated. If you know there is only one task, and it is idle
    waiting for the interrupt, you can use its stacks, but what then is the
    purpose of using interrupts? Why not just poll?

    To a limited extent you can get away with pushing something temporarily
    on whatever is the current data stack, provided you get it off again
    before exiting the ISR. But that's a far cry from executing high level
    Forth.

    And, of course you want interrupts to occur during executing of assembly
    language words in an ITC Forth. A lot of your kernel may be written
    that way. If all your primitives, so identified because those are the
    ones you want to run fast, have to disable and re-enable interrupts
    that's just more overhead.

    To me, the point of having an interrupt is to be able to respond to an
    event in the shortest amount of time and with minimum impact on the rest
    of the running application. On our systems, you can get into an ISR in
    one or two instruction times, and out in one. Why add further overhead?

    Cheers,
    Elizabeth

    --
    ==================================================
    Elizabeth D. Rather (US & Canada) 800-55-FORTH
    FORTH Inc. +1 310.999.6784
    5959 West Century Blvd. Suite 700
    Los Angeles, CA 90045
    http://www.forth.com

    "Forth-based products and Services for real-time
    applications since 1973."
    ==================================================
     
    Elizabeth D Rather, May 22, 2011
    #4
  5. wzab

    Arlet Ottens Guest

    I presume that there is some sort of 'current task' that points to the
    task currently executing, and that you can find the current stack from
    there. Alternatively, use a dedicated interrupt stack.
    Why for a limited extent ? And of course you have to pop everything from
    the stack that you put on.
     
    Arlet Ottens, May 22, 2011
    #5
  6. My point is that all of what you describe adds unnecessary overhead. If
    you follow the model I described above, you only need a few instructions
    in your ISR (typically 3-5 for a simple "read a value and put it
    somewhere"), and it would cost more than that just to get into a
    high-level Forth routine. Much easier to just do the bare minimum at
    interrupt time and let the task responsible for the interrupting device
    do the high level processing.

    Cheers,
    Elizabeth

    --
    ==================================================
    Elizabeth D. Rather (US & Canada) 800-55-FORTH
    FORTH Inc. +1 310.999.6784
    5959 West Century Blvd. Suite 700
    Los Angeles, CA 90045
    http://www.forth.com

    "Forth-based products and Services for real-time
    applications since 1973."
    ==================================================
     
    Elizabeth D Rather, May 22, 2011
    #6
  7. wzab

    Arlet Ottens Guest

    If you defer the work to a task, you have the extra overhead of context
    switches, which are generally more expensive than saving state in the
    ISR. If you can do all the work in the ISR, or at least do all the work
    in the majority of cases, this actually costs less overall.
     
    Arlet Ottens, May 22, 2011
    #7
  8. wzab

    Stephen Pelc Guest

    Adding native Forth ISRs (Interrupt Service Routine) to a threaded
    code Forth is an exercise in futility. If you want to write an ISR in
    Forth you need speed. If you need speed, you need an optimising native
    code compiler. These are provided by Forth vendors such as MPE and
    Forth Inc.

    Speaking for MPE, I haven't written an ARM or Cortex ISR in anything
    but high-level Forth for over ten years. The VFX code generator does
    a very good job.

    The downside of such tools is that they are neither Open Source nor
    free (beer). However, the MPE ARM/Cortex Stamp edition compiler for
    ARM and Cortex is not expensive
    GBP 75, EU 87, USD 124
    and supports LPC17xx directly. There's a limit of 120kb Flash and
    64kb RAM.

    Stephen


    --
    Stephen Pelc,
    MicroProcessor Engineering Ltd - More Real, Less Time
    133 Hill Lane, Southampton SO15 5AF, England
    tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
    web: http://www.mpeforth.com - free VFX Forth downloads
     
    Stephen Pelc, May 22, 2011
    #8
  9. wzab

    Stephen Pelc Guest

    Although I agree with much of what you say in theory, I'm going to
    disagree with you here in relationship to modern CPUs such as ARM
    and Cortex.

    When you have a really good Forth compiler, the performance enabled
    by it permits us to write more complex interrupt handlers in high
    level Forth. The example I always use was a four channel 9600 baud
    bit-banged serial driver fitted to a vending machine. It ran from a
    38400 Hz timer interrupt and had a worst case interrupt duration
    (measured) of 1.5us on a 60MHz LPC2148. On a Cortex LPC17xx, it
    would be faster still.

    A really good Forth compiler is enabling technology.

    Stephen


    --
    Stephen Pelc,
    MicroProcessor Engineering Ltd - More Real, Less Time
    133 Hill Lane, Southampton SO15 5AF, England
    tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
    web: http://www.mpeforth.com - free VFX Forth downloads
     
    Stephen Pelc, May 22, 2011
    #9
  10. Isn't this overly pessimistic? Compared to interrupting an
    arbitrary program (and provided there is one program space)
    you need to save two less registers, containing the return
    stack pointer and the data stack pointer. Instead, use them,
    which saves initialisation. They must balance out on return.
    This assumes that the Forth uses its stacks properly, i.e. no data is
    stored outside of the area "protected" by the data stack pointer. 1)
    For the rest the usual caveat's for interrupts apply: save all registers
    that you use.
    By the way, in some processors (z80) you had no choice, interrupts
    just use the z80-stack, which Forth uses undoubtedly either as data
    or return stack.

    So what you need to do in the end is store the code start
    address of a Forth word that is executed upon interrupt
    in the interrupt table. This is probably a code word that save
    registers.
    You also need a code word that restores and does a return from
    interrupt. The interrupt handler better end with a call to that word.
    If the above facilities are not in the Forth already, you can't get
    around writing assembler. Then you might as well take this approach.

    1) You have to take that into account anyway, whenever interrupts
    are used.
     
    Albert van der Horst, May 22, 2011
    #10
  11. wzab

    wzab Guest

    Thanks for all replies.
    I have rather small experience with writing Forth programs, but quite
    good in writing Linux drivers in C.
    Usually the interrupt is not handled in context of particular task. It
    simply uses the protected mode stack of task which was runing when
    interrupt occured.
    Could it be done in similar way in Forth?
    When interrupt occurs in the assembly language we simply switch
    interrupts off (as the hardware source of interrupt may be still
    active) and set a global flag informing that the interrupt is pending,
    thets all.
    Then the inner Forth interpreter after completion of the current
    word(?) should execute the Forth word responsible for servicing the
    interrupts. This word would use the stacks of the current, random
    task. If interrupt is associated with data transfer, then tha data
    should be read/written to/from a global variable (or could it be a
    task specific variable, if particular task registers the interrupt
    routine?).
    The question is if I can assume, that the latency associated with
    waiting to the end of execution of current word will be sufficiently
    small?

    If the above approach is correct, then interrupt handling in Riscy
    Pygness should be associated with:
    1.
    Adding of simple "universal" interrupt handle and "inInterrupt" flag.
    2.
    Quite a small modification of nxTab routine - if the inInterrupt flag
    is set, before loading of the new word:
    ldrh W, [IPTR], #2 ; read unsigned half-word then bump IPTR
    by 2
    We should store the old IPTR value (on return stack?), load the IPTR
    with the address of our high level interrupt routine and clear
    the inInterrupt flag.
    The high level interrupt routine should inactivate the hardware
    interrupt source (e.g. read the data from the serial buffer) and
    enable interupts (if we allow nested interrupts, if not - exiting of
    high level interrupt routine should reenable interrupts).
    At the end of high level interrupt routine the previous value of
    IPTR should be restored from the return stack (so the interrupt
    servicing word should have the bit 0 ("jump flag") cleared ).

    Is the above described implementation reasonable, or have I missed
    something?
     
    wzab, May 22, 2011
    #11
  12. wzab

    Rocky Guest

    To digress slightly: You would get better distortion tolerance
    (symmetrical 33%) using 28800Hz interrupt. The 4*sampling gives
    asymmetrical (25-50% and 50-75% on the delay after the start pulse.
    28800 vs 38400 interrupts per second also reduces overhead.
     
    Rocky, May 22, 2011
    #12
  13. Yes, that's essentially how we do it, except that some processors
    (particularly low-end embedded microcontrollers) don't have a "protected
    mode".
    If it's more than a single machine instruction, the latency is longer
    than the approach I'm advocating. Obviously, it depends on the word
    being executed, which could be anywhere from a microsecond to much, much
    longer. In most real-time applications, you can't afford that level of
    uncertainty for the time-critical part of an interrupt handler.

    Our approach separates the time-critical response from the higher-level
    processing, which can certainly take place in high-level Forth, at the
    task level.

    Consider the data acquisition situation. An interrupt signals the
    presence of a value on the A/D. The interrupt code reads the value,
    stores it in a buffer the application has provided, and sets up the next
    conversion. It then sets the flag that enables the task responsible for
    the data to wake up and process the value. This needn't take more than
    a very few instructions, needs no stacks, and (depending on the
    processor) may not even need any registers.

    The actual logic of processing the data may be anywhere from simple to
    complex. If the data is coming in faster than the task can process it,
    it can go into a buffer. That adds management of buffer pointers to the
    ISR, but that isn't hard, either.

    Cheers,
    Elizabeth

    --
    ==================================================
    Elizabeth D. Rather (US & Canada) 800-55-FORTH
    FORTH Inc. +1 310.999.6784
    5959 West Century Blvd. Suite 700
    Los Angeles, CA 90045
    http://www.forth.com

    "Forth-based products and Services for real-time
    applications since 1973."
    ==================================================
     
    Elizabeth D Rather, May 22, 2011
    #13
  14. In the first place, the interrupt routine needs to save *only* the
    registers that it uses, which may be as few as none, depending on the
    processor and what you have to do. And I agree that you need to leave
    the processor exactly as you found it.

    But you're missing the point that to run high-level Forth you need more
    things than two stack pointers. There are other registers, user
    variables, and other resources. This makes instantiating a Forth
    environment costlier than simply branching to a few machine instructions.

    Cheers,
    Elizabeth


    --
    ==================================================
    Elizabeth D. Rather (US & Canada) 800-55-FORTH
    FORTH Inc. +1 310.999.6784
    5959 West Century Blvd. Suite 700
    Los Angeles, CA 90045
    http://www.forth.com

    "Forth-based products and Services for real-time
    applications since 1973."
    ==================================================
     
    Elizabeth D Rather, May 22, 2011
    #14
  15. Not necessarily. In the system I am currently working on, breaking at
    ramdom many times puts me in the kernel code, in the middle of
    switching staks between tasks.
    Thta's always a cleaner solution.
     
    Roberto Waltman, May 22, 2011
    #15
  16. The thing about this discussion that bothers me is talking about
    interrupts in the abstract. But in my experience how you handle
    interrupts depends entirely on what is causing the interrupt. There
    is a huge difference between an interrupt indicating the user pressed
    a button on a panel versus an interrupt coming in from an Ethernet
    controller receiving packets at full speed. And there is a huge
    difference between a RTC interrupt ticking away every second versus
    driving a matrix of LEDs that you're pulse-width modulating brightness
    at 1kHz. Sometimes an interrupt comes in from human-speed events and
    those are so slow you don't care if it takes a few milliseconds here
    or there. Other times not responding to an interrupt fast enough
    means you're not going to be transferring data fast enough on a
    channel or that small perturbations in your timing will be easily
    detected by human senses.

    So I think we need to factor at least three things into this
    discussion:

    1. How quickly you need to react to an interrupt.
    2. How many cycles your interrupt service routine will take.
    3. The rate of those interrupts.

    Yeah, the interrupt mechanism is exactly the same, but the question of
    overhead starts to matter more.

    Personally, I do tend to code my ISRs in a high-level language. But I
    take a "trust but verify" approach and look at the quality of the code
    generated. And on critical interrupts, you know I'm going to be
    instrumenting my code and hooking up a 'scope to measure exactly how
    much time (and what percentage of time that is from the larger
    application) is taken. These things matter, and regardless of how one
    chooses to implement an ISR, you better have a very good understanding
    of the overheads, resources used, and other limitations.
     
    John Passaniti, May 22, 2011
    #16
  17. In my experience, there's usually some component of the response that's
    time-critical and some that isn't. In the case of a clock tick, that
    event has to be registered immediately, but there isn't any further
    processing to occur. In the case of receipt of an interrupt that
    signals the completion of a process that some task has been waiting for
    (e.g. a disk read) notifying the task is all that has to be done, and
    *all* the subsequent activity is at the task level. My objective is to
    handle the time-critical steps, whatever they are, *without delay*,
    which means without scheduling a task or instantiating a context for
    high-level Forth (and I agree with Stephen that trying to do that for an
    ITC Forth makes no sense at all). There is no overhead involved in
    entering an ISR in our systems.

    In a multitasking Forth, context switches are occurring, but (at least
    in the implementations I'm familiar with) they are vastly less costly
    than in more conventional multi-tasked executives: typically on the
    order of half-dozen machine instructions to activate a task and fewer to
    de-activate one.

    Cheers,
    Elizabeth

    --
    ==================================================
    Elizabeth D. Rather (US & Canada) 800-55-FORTH
    FORTH Inc. +1 310.999.6784
    5959 West Century Blvd. Suite 700
    Los Angeles, CA 90045
    http://www.forth.com

    "Forth-based products and Services for real-time
    applications since 1973."
    ==================================================
     
    Elizabeth D Rather, May 22, 2011
    #17
  18. wzab

    wzab Guest

    Well, so this is an approach which is used in interrupt handlers e.g.
    in Linux - servicing of interrupt is split between the ISR which runs
    as fast as possible and handles most critical actions related to
    interrupt (e.g. copying data from hardware register, where they could
    be overwriten by the next received data, to the buffer), and the
    deferred interrupt routine ("bottom half", "tasklet", "workqueue"
    whatever implementation will be used).

    What I wanted to achieve was to give my students a way to experiment
    with LPC1769 based system, including interrupts, without continuous
    reflashing of CPU. The Riscy Pygness Forth seems to be an ideal
    solution, however I need a way to handle interrupts completely on the
    Forth level. Additionally I'd like to hide as few details of interrupt
    handling as possible.
    Therefore approach with blocking of interrupts in ISR and passing
    control to the high level Forth word (which may be defined by user in
    interactive session) seemed very good.
    As this will be used mainly for didactical tasks, reasonable decrease
    of performance may be accepted.
     
    wzab, May 22, 2011
    #18
  19. wzab

    rickman Guest

    I have to say I am lost. The description you give above only
    describes the division between the ISR and the non-ISR code for
    dealing with an ADC. I don't see anything that relates to the issue
    of implementing the ISR in Forth.

    Such a simple ISR can use the stack of what ever process it
    interrupted, as you say, as long as it doesn't need to store anything
    on the stack. I expect that is a given and the ISR would use static
    variables for such items.

    You keep referring to the "overhead" of setting up a Forth
    environment. I guess I don't have the insight into a complex Forth
    that would work that way. I am picturing a very simple Forth that has
    a single task of main line code and interrupts. I don't know what
    would be required for multitasking other than multiple stacks. I'm
    not sure of the capabilities of Riscy Pygness.

    Do you really think this is such an absolute that ISR in Forth is
    universally a bad idea?

    Rick
     
    rickman, May 22, 2011
    #19
  20. wzab

    rickman Guest

    Ah, now I get it. You are thinking in terms of the FULL Forth
    environment. That is not needed in an ISR. Typically and ISR for an
    embedded application only needs to do some basic memory access and
    IO. If you are talking about a UNIX system, then maybe more is
    required. But for the OP, clearly only a subset is needed that does
    not require that level of overhead.

    Rick
     
    rickman, May 22, 2011
    #20
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.