What is the underlying mechanism behind RO or WO and WR registers?

It's all about how the registers are connected to the bus:

if wb_cyc = '1' and wb_stb = '1' then         -- accessing the register

    if wb_we = '1' then                       -- writing to the register?
        my_reg <= wb_dat_i;                   -- update register contents

    else                                      -- reading from the register
        wb_dat_o <= my_reg;                   -- return register contents
    end if;
end if;

If you leave out the part of the hardware that can read the register then your register is write-only. Similarly if you leave out the part of the hardware that can write to the register then it's read-only.

Sometimes it makes sense to have only one "direction" of data flow. Think of a DAC or an ADC data path.

Also, sometimes it makes sense to have the same address return something other than the data you've written to it. Think of most common microcontrollers; their peripherals tend to have a single address that is a "command" for writes and a "status" for reads.

Another example would be interrupt flags: you read the interrupt register to see which peripheral interrupted the program, then write a '1' to the same bit to clear the interrupt flag. One way to realize that is as follows:

wb_dat_o <= (others => '0');
if wb_cyc = '1' and wb_stb = '1' then             -- register access
    if wb_we = '1' then                           -- writing to the register?
        clear_int <= wb_dat_i(0);
    else                                          -- reading from the register?
        clear_int <= '0';
        wb_dat_o <= running & int_enabled & int_active;
    end if;
end if;

Here you can see that I'm setting a flag to whatever is in bit position 0 in the write case (and I don't care about whatever else they may have written), while in the read case I'm building up a status word made up of individual bits that the peripheral is driving.

aside: I am also explicitly maintaining clear_int at zero in the read case. It's just good coding practice to make sure that all signals are assigned in all code paths in HDL. At best not doing so makes for sloppy code. At worst you can infer latches. Note that I do something similar with wb_dat_o before I even enter the register access case.

Keep in mind that these are only examples to illustrate the point, and that a well-written program will be described more carefully and thoroughly. Also keep in mind that it's 8am here and I haven't had my coffee yet. :-)


I guess you are talking about register in some peripheral part, either part of the processor chip or connected to it in some way.

In a Read Only register the data originates from some external (from the processor's point of view) process and associated hardware, like the receiving of serial data by a UART. Hence that hardware connects to te FF's input, and its outputs connect (for instance) to the processor's bus.

For a Write Only register the situation is the reverse, the processor connects (directly or indirectly) to the FF's inputs, and the FF's outputs connect to some external circuitry. An example is a digital-to-analog converter.


One thing I would suggest you try to do, though alas many designs don't do this consistently, is to divide writable registers into "latching" and "action" categories, with the expectation that a read of a latching register will always return the value written, while a read of an action register's address should be regarded as reading an independent read-only register.

For example, suppose your system has eight latches that detect external events. If those latches sit in one 8-bit register which can be directly read or written by the CPU, or which can be asynchronously set externally, then if user code observes that bit 0 and 2 are set (meaning the register reads 0x05) and wants to clear bit 0 while leaving bit 2 set, trying to write 0x04 to the register just as some event would cause some other bit to become set would have the unfortunate effect of clearing the latch associated with that other event, thus causing it to be missed. If one instead had a "read" address that reports the state of the latches, a "write zeroes" address which allows selected bits to be cleared, and possibly a "write ones" address which allows selected bits to be set, then these problems would not occur.

For things that the CPU is actually supposed to control (as opposed to merely making note of--as was the case with interrupt flags), but where the CPU's ability to control them may be limited by external factors, one should separate out what the CPU is requesting from what it's able to do. For example, if a motor-control output is supposed to be asynchronously switched off by a an external "motor-shutdown" input, that input shouldn't simply clear the "motor-control" output latch which the CPU set. Instead, the CPU should have a latch which says whether it wants the motor to be on, and that latch should not be changed by external factors. A second externally-triggered latch should indicate whether a motor-shutdown signal has arrived; the motor output should then be driven only when the CPU request says "go" and the "shutdown" latch is not tripped, thus allowing the CPU to have separate "commands" for "start this device which it had previously requested to be off, assuming no fault exists", and "acknowledge/clear the fault condition".

Designing hardware to separate out what the computer is asking for versus what the external hardware is currently allowed to do shouldn't be hard, and keeping such things separate as a matter of principle will help avoid bugs that can occur when the CPU--in the process of trying to write one thing--accidentally writes something else.