tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Waiting for a bit in a register to be cleared: which strategy?



> In order to read register A, I need to wait that a bit is cleared in
> register B [...].

> As a temporary solution, I chose a brutal while with a brutal 32 bit
> wise check:

> #define NOT_CLEARED	0x0000001

> while ((bus_space_read_4(mytag, myhandle, REG_A))
> 	& (uint32_t) NOT_CLEARED) {
> }

I'm not sure what's "brutal" about those, but OK...

> result = bus_space_read_4(mytag, myhandle, REG_B);

> This works only because REG_A has all 0s except the LSB: when the LSB

Actually, because of your "& (uint32_t) NOT_CLEARED", it works
regardless of the other bits in register A: you are masking off all but
the low bit.

> But I am actually interested only in the LSB and would like to
> disregard the other 7 bits in the register.

The other 31 bits - you're using bus_space_read_4, so it's reading 31
bits.  If register A is an 8-bit register, you conceptually should be
using bus_space_read_1.  (Depending on the device, the bus, and the
like, this may or may not make a difference in practice.)

> What could it be the most efficient way to accomplish this?

Efficient in terms of what resource?  That is, what measure of
efficiency are you interested in here?  With two exceptions (see
below), I'm having trouble thinking of one for which there's anything
wrong with what you have.

> When a bit must be set, there's the macro __BIT(n) in

>  <https://nxr.netbsd.org/xref/src/sys/sys/cdefs.h#640>

> Is there something similar for when just a single bit must be read?

Check out the definition of __BIT - it is no different in practice
from more or less what you're already doing.

> This way, if for some reason the LSB in REG_A is never cleared (the
> device is not working, or similar), the while never exits.

Correct.

> Is it available, inside the kernel, some function like sleep, or
> wait, so that a maximum timeout can be set?

There are multiple ways to do this.

The simplest is probably to just put a limit on the iteration count:

 int loops;

 for (loops=1000000;loops>0;loops--)
  { if (! (bus_space_read_... & NOT_CLEARED)) break;
  }
 if (loops > 0)
  { ...the bit cleared...
  }
 else
  { ...timed out...
  }

But this makes the timeout inherently dependent on the host CPU speed.
If that's a problem, you could add a delay in the loop

 int loops;

 for (loops=1000;loops>0;loops--)
  { if (! (bus_space_read_... & NOT_CLEARED)) break;
    DELAY(100);
  }
 if (loops > 0)
  { ...the bit cleared...
  }
 else
  { ...timed out...
  }

(this example waits approximately 100ms - 1000 loops at 100us each).
This will increase the delay from the bit changing to your code
noticing by an effectively random amount from zero to the delay time.

If the device can be made to generate an interrupt when the bit
changes, another answer is to make your driver enable that interrupt
and go to sleep waiting for it.  The changes for that are too large for
me to give here; depending on the surrounding code structure, they may
involve significant rearrangement.

Yet another answer, particularly applicable if the delay is likely to
be long and the loop is running in a process context or is otherwise
able to sleep, is for it to sleep, waking up to check every clock tick,
or every second, or however often you want.  This, too, may involve
nontrivial code rearrangement.

The last two options have the advantage that they release the CPU to do
other useful work while waiting.  They have the disadvantage that they
decrease responsiveness - the delay from the bit changing to the code
reading register B increases significantly.

> Summarising, my guess for the best solution in this case is: reading
> a single bit with something similar to __BIT and set a maximum amount
> of time for the while cycle.

Most hardware (CPUs, buses, etc) do not support reading a single bit of
a register.  Indeed, in some cases you cannot, for example, read just
the low 8 bits of a 32-bit register - the hardware doesn't support it.
(Is yours such a case?  I don't know; you haven't given enough details
for me to tell, and I probably don't know the answer for your hardware
in any case.)

I wrote, above, that there are only two ways I could think of in which
there's anything wrong with what you wrote.

One is that busy-waiting is inefficient in that it does not allow the
CPU to do any other useful work while waiting; if the delay is long
compared to the time required to context-switch to doing other useful
work, and there is, or might be, other useful work to do, this can be
significant.

The other is that, as I wrote, for some buses and some devices, you
have to access a register as the correct width or it will not work: if
the register is 16 bits wide, you have to do a 16-bit read (or write)
or the hardware doesn't work right - depending on the details, it might
act as though it's not there (probably crashing the system), or it may
return meaningless bits on reads and ignore writes, or it could even
access a different register (I have never seen this done, but on some
buses it certainly could be done).  You wrote of "the other 7 bits",
indicating that you are thinking of the register as 8 bits wide, but
you used bus_space_read_4, so there is an apparent mismatch.  I don't
know enough about your setup to tell whether it's an actual mismatch,
nor, if it is, whether the difference matters.

/~\ The ASCII				  Mouse
\ / Ribbon Campaign
 X  Against HTML		mouse%rodents-montreal.org@localhost
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B


Home | Main Index | Thread Index | Old Index