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 &lt;= wb_dat_i;                   -- update register contents

    else                                      -- reading from the register
        wb_dat_o &lt;= 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 &lt;= (others =&gt; '0');
if wb_cyc = '1' and wb_stb = '1' then             -- register access
    if wb_we = '1' then                           -- writing to the register?
        clear_int &lt;= wb_dat_i(0);
    else                                          -- reading from the register?
        clear_int &lt;= '0';
        wb_dat_o &lt;= running &amp; int_enabled &amp; 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. :-)