I/O

Thread Starter

nikascom

Joined Jan 29, 2020
2
Hello. I have questions about implementation of I/O.

When a CPU reads/writes using IN or OUT, all data is delivered to devices (their registers or memory) directly. How are port address divided between devices? And how is this data delivered to a device (are there any agreements)?
 

ronsimpson

Joined Oct 7, 2019
3,037
When a CPU reads/writes using IN or OUT
This depends on what type of computer. Some CPUs address memory with one type of instruction and IO with a different type of instruction. (example: memory might have a 16 bit address while IO only a 8 bit address) Other CPUs address everything as memory. But there is a small chunk of memory that is connected as IO. In this case memory & IO are reached in the same way.

I think this is much like "registers". Some CPUs have memory mapped registers. example memory 0 &1 form a 16 bit register. But other CPUs have registers mapped as "A, B, C etc." which are not in memory. I remember a CPU that has a 16 bit address for "memory" but also has a 8 bit address for the lowest block of memory. In this case reading memory (0005 hex) is the same thing as reading memory (05 hex). Some of the low 256 memory locations are IO.
 

dl324

Joined Mar 30, 2015
16,921
Welcome to AAC!
When a CPU reads/writes using IN or OUT, all data is delivered to devices (their registers or memory) directly. How are port address divided between devices?
It would be helpful if you were more specific about what you mean by CPU. CPU is generally used to refer to microprocessors that don't have general purpose I/O. Microcontrollers are microprocessors that have general purpose I/O (and other features integrated).

The two ARM microcontrollers I've used (Allwinner and the ones used by RPi) can map I/O to memory addresses. I/O can be also be controlled by other interfaces (e.g. sysfs).
 

Thread Starter

nikascom

Joined Jan 29, 2020
2
Welcome to AAC!
It would be helpful if you were more specific about what you mean by CPU. CPU is generally used to refer to microprocessors that don't have general purpose I/O. Microcontrollers are microprocessors that have general purpose I/O (and other features integrated).

The two ARM microcontrollers I've used (Allwinner and the ones used by RPi) can map I/O to memory addresses. I/O can be also be controlled by other interfaces (e.g. sysfs).
I meant x86 (i386). I made a progress and understood how I/o signal from a CPU (i386) is delivered to a controller. CPU raises a IO line, so the data is delivered to a bus with controllers and each of the controllers checks if the address belongs to it. This was about IO mapped IO (isolated IO).
As far as I know x86 also supports MMIO. So for me it’s interesting how an io signal is taken from memory and delivered to a controller or device (in case if this address is mapped to a device).
For example we have a command mov 555h, byte 23. And this 555h is mapped to a sound blaster. Is there a special controller which can check addresses and send this data to a device if needed? I’M interested in how this is implemented on the hardware level (transfer of data from mem to a device in case of MMIO)
 

nsaspook

Joined Aug 27, 2009
13,273
I meant x86 (i386). I made a progress and understood how I/o signal from a CPU (i386) is delivered to a controller. CPU raises a IO line, so the data is delivered to a bus with controllers and each of the controllers checks if the address belongs to it. This was about IO mapped IO (isolated IO).
As far as I know x86 also supports MMIO. So for me it’s interesting how an io signal is taken from memory and delivered to a controller or device (in case if this address is mapped to a device).
For example we have a command mov 555h, byte 23. And this 555h is mapped to a sound blaster. Is there a special controller which can check addresses and send this data to a device if needed? I’M interested in how this is implemented on the hardware level (transfer of data from mem to a device in case of MMIO)
The x86 has a boatload of memory modes so exactly how MMIO works depends on the mode.
The old PC bios memory mode uses the default CPU 'Real Mode' memory map.
https://wiki.osdev.org/Memory_Map_(x86)

No one stays in 'Real Mode' very long so a switch to protected mode is needed.
https://wiki.osdev.org/Protected_Mode
http://www.windowsscope.com/virtual-memory-and-address-translating-for-x86-and-x86-pae/

Here is a Linux protected memory mode intro from a few years ago.
http://140.120.7.21/LinuxRef/mmLinux/Linux_memory_model.html

A typical Linux Kernel driver peek/poke using VM access to MMIO physical memory at the current process MMIO address (mem-mapped into the local kernel memory space by the VM system) of the device.

This C code fragment returns a kernel memory iobase ( dev->iobase = link->resource[0]->start;) for a PCMCIA card for digital i/o and analog inputs.
C:
/* daqcard700 registers */
#define DIO_W        0x04    /* WO 8bit */
#define DIO_R        0x05    /* RO 8bit */
#define CMD_R1        0x00    /* WO 8bit */
#define CMD_R2        0x07    /* RW 8bit */
#define CMD_R3        0x05    /* W0 8bit */
#define STA_R1        0x00    /* RO 8bit */
#define STA_R2        0x01    /* RO 8bit */
#define ADFIFO_R    0x02    /* RO 16bit */
#define ADCLEAR_R    0x01    /* WO 8bit */
#define CDA_R0        0x08    /* RW 8bit */
#define CDA_R1        0x09    /* RW 8bit */
#define CDA_R2        0x0A    /* RW 8bit */
#define CMO_R        0x0B    /* RO 8bit */
#define TIC_R        0x06    /* WO 8bit */
/* daqcard700 modes */
#define CMD_R3_DIFF     0x04    /* diff mode */

/*
* Data acquisition is enabled.
* The counter 0 output is high.
* The I/O connector pin CLK1 drives counter 1 source.
* Multiple-channel scanning is disabled.
* All interrupts are disabled.
* The analog input range is set to +-10 V
* The analog input mode is single-ended.
* The analog input circuitry is initialized to channel 0.
* The A/D FIFO is cleared.
*/
static void daq700_ai_config(struct comedi_device *dev,
                 struct comedi_subdevice *s)
{
    unsigned long iobase = dev->iobase;

    outb(0x80, iobase + CMD_R1);    /* disable scanning, ADC to chan 0 */
    outb(0x00, iobase + CMD_R2);    /* clear all bits */
    outb(0x00, iobase + CMD_R3);    /* set +-10 range */
    outb(0x32, iobase + CMO_R);    /* config counter mode1, out0 to H */
    outb(0x00, iobase + TIC_R);    /* clear counter interrupt */
    outb(0x00, iobase + ADCLEAR_R);    /* clear the ADC FIFO */
    inw(iobase + ADFIFO_R);        /* read 16bit junk from FIFO to clear */
}

static int daq700_auto_attach(struct comedi_device *dev,
                  unsigned long context)
{
    struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
    struct comedi_subdevice *s;
    int ret;

    link->config_flags |= CONF_AUTO_SET_IO;
    ret = comedi_pcmcia_enable(dev, NULL);
    if (ret)
        return ret;
    dev->iobase = link->resource[0]->start;

    ret = comedi_alloc_subdevices(dev, 2);
    if (ret)
        return ret;

    /* DAQCard-700 dio */
    s = &dev->subdevices[0];
    s->type        = COMEDI_SUBD_DIO;
    s->subdev_flags    = SDF_READABLE | SDF_WRITABLE;
    s->n_chan    = 16;
    s->range_table    = &range_digital;
    s->maxdata    = 1;
    s->insn_bits    = daq700_dio_insn_bits;
    s->insn_config    = daq700_dio_insn_config;
    s->io_bits    = 0x00ff;

    /* DAQCard-700 ai */
    s = &dev->subdevices[1];
    s->type = COMEDI_SUBD_AI;
    s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
    s->n_chan = 16;
    s->maxdata = BIT(12) - 1;
    s->range_table = &range_daq700_ai;
    s->insn_read = daq700_ai_rinsn;
    daq700_ai_config(dev, s);

    return 0;
}

// write and read I/O bits on the DAQ device

static int daq700_dio_insn_bits(struct comedi_device *dev,
                struct comedi_subdevice *s,
                struct comedi_insn *insn,
                unsigned int *data)
{
    unsigned int mask;
    unsigned int val;

    mask = comedi_dio_update_state(s, data);
    if (mask) {
        if (mask & 0xff)
            outb(s->state & 0xff, dev->iobase + DIO_W);
    }

    val = s->state & 0xff;
    val |= inb(dev->iobase + DIO_R) << 8;

    data[1] = val;

    return insn->n;
}
 
Last edited:
Top