3

I have one high priority interrupt which sends USB data, and one lower priority task which already fetches the next data to be send.

Sometimes the high priority interrupt requires some data that is still being fetched, and in that case I need to instruct the MCU to finish the lower priority task before continuing execution in the high priority interrupt.

I can't figure out how to make this work. Is it possible to raise the priority of the background task higher using NVIC_SetPriority, and immediately call NVIC_SetPendingIRQ from the USB task, and after that lower it again? Or what would be the simplest way to make this work?

artless noise
  • 21,212
  • 6
  • 68
  • 105
Maestro
  • 9,046
  • 15
  • 83
  • 116
  • I find it very impolite to provide no feedback after someone else invested his time to try to help you with the problem. – Adi Feb 14 '14 at 11:43
  • @Adi I find it very impolite to downvote questions, when your answer doesnt get accepted in a couple of hours. I have to at least think about your answer first, before I can decide if it helps me further, but currently it doesn't. – Maestro Feb 14 '14 at 18:48

2 Answers2

1

How much time do you have to answer the data request, and how long does it take to prefetch for the next one? If the prefetch time is short, I would reverse your priority on your interrupts - this keeps the buffer filled for the data request interrupt.

Otherwise there isn't a clean way to do what you want bare-metal - this is what Operating Systems are for. If you are in an OS, the data request interrupt routine can request a signal from the prefetch interrupt routine and return from interrupt and wait for the data request interrupt routine to send a signal that it has completed a block.

Bare-metal, you could try having the prefetch routine call the data request interrupt after each buffer is ready. The DRIR then does a series of checks when it wakes up

  • Was I woken by a Data Request?
    • yes: Do I have data to send?
      • yes: Send data, clear interrupt request, return from interrupt
      • no: Increment "blocks needed" counter by 1, clear interrupt request, return from interrupt
    • no: must have been woken by a Prefetch complete, is "blocks needed" zero?
      • yes: buffer has data, but not needed yet, return
      • no: Send 1 block of data, decrement "blocks needed" until it hits zero or buffer is empty, return

There isn't any guarantee you will get the data out in time, but at least this way there's a chance for the lower priority interrupt to finish.

BTW, I don't think the NVIC can force a currently-executing interrupt to stop for a different higher-priority one. The priorities really matter when the interrupts occur at the same time (or occur when interrupts are already masked, ie when servicing another interrupt).

Many operating systems provide a two-step interrupt process where the direct interrupt routine is minimal as possible to clear the interrupt, and it notifies a separate interrupt thread to handle the longer, detailed parts of the request. See http://en.wikipedia.org/wiki/Interrupt_handler

Since the direct interrupt routine is small&fast, it allows for priorities to be assigned to the respective interrupt threads to control execution order.

0

I don't know if you have a good reason to run RX and TX in two different contexts, but usually this could be very simply implemented in only one context. However, if you really want to follow your original design, you need to introduce some kind of a mechanism to synchronize operations of these two contexts. Normally, if you're running the RTOS, you would use event flag, binary semaphore or some other similar service which is provided by the given RTOS.

You would not want (and could not) send any data before you receive some, right? That's why you would need to wait for a notification (in TX context) which is to be set from RX context after the data was received.

Having done this kind of tunneling without employing this technique would go out of sync very soon.

Adi
  • 1,296
  • 10
  • 13
  • Its not so much RX/TX. The high priority interrupt is coming from Windows fetching disk sectors (my firmware is a USB mass storage device), and the low priority interrupt is manually started by me to already buffer the next couple of sectors, in case they will be requested later. However, sometimes Windows requests them too fast, and my 'smart buffering' is not completely done yet. In that case I want to wait untill the lower priority task is finished, but I cannot just wait in the high priority interrupt because it blocks the lower one. I hoped there was some ARM instruction to let it finish. – Maestro Feb 15 '14 at 23:41
  • 1
    You can just simply disable the USB related interrupts while you are in a lower priority task and re-enable them after you finish with it. However, this will obviously cause USB requests, which arrived in the meantime, not to be serviced. If you want to avoid this as well, I would consider redesigning the FW architecture. – Adi Feb 17 '14 at 16:31
  • That is how I do it now, i give them equal priority so they block eachother, but that decreases transfer performance because nothing can be written from memory to the USB endpoint while Im fetching the next block. I need to alternate between the two interrupts, and if there is no ARM instruction for that, I don't see how a RTOS could make any difference for this use-case. – Maestro Feb 18 '14 at 18:11
  • Instead of blocking the higher priority interrupt, have it decide if it can proceed or if it must wait on the result of the other. If it must wait, then either take no action at that time and wait for another read attempt, or else setup something to run once the other is finished (you'll have to figure out what is legal/optimal for USB mass storage or whatever it is you are implementing) – Chris Stratton Feb 18 '14 at 19:56
  • @ChrisStratton I cannot ignore the read request, the USB driver (which is in ROM so cannot be modified) asks for a pointer to the sector data. If I dont fill the data it will return an sector with zeroes to Windows, and Windows thinks it was valid. There is no way to return an error code or let the OS retry. I think its a limitation of the driver NXP builds in their Cortex MCU's, but I have to deal with it Im afraid or write a new USB stack from scratch, which is a massive task. – Maestro Feb 19 '14 at 10:21
  • 1
    You should have put this info in your question as well. I thought that you had *normal* USB stack and that's why I proposed changing the FW architecture. Having a ROM-ized USB stack, while trying to optimize the USB-MSD throughput, you could easily be hit by the (non)-flexibility of that stack. You would need to investigate if stack is even designed so that it is feasible to tweak it in that way. – Adi Feb 19 '14 at 12:36
  • Otherwise, another suggestion would be to give it a try with simply another USB stack and see if it matches your throughput expectations. I have stumbled upon a few open source USB stacks related to the LPC uC's, which I assume you are using. – Adi Feb 19 '14 at 12:37
  • @Adi Its certainly a limitation of the stack, because previously I was using nxpUSBlib (which is based on LUFA) and I didnt have this issue, because it requested larger blocks of sectors. Then I switched to the ROM drivers, and ran into this problem. But I was trying to find a work-around instead of switching back to the LUFA based stack. – Maestro Mar 04 '14 at 20:39