Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

RISC

Name: Anonymous 2011-09-22 9:29

/prog/, I'm working on a RISC instruction set that is more RISC than any RISC out there. I've devised an instruction format, plus fifteen core instructions that should suffice for any programming out there. The instruction format, instruction forms, and instructions can be found here:

http://jsbin.com/ekuwap

Any comments or suggestions?

Name: Anonymous 2011-09-22 9:41

Looks interesting.

Name: Anonymous 2011-09-22 9:44

Seems fine, although you're wasting 4 bits on the opcode there, however they're probably well-wasted as it makes tools (assembler/disassembler/...) easier to write.

Name: Anonymous 2011-09-22 9:52

Fucking terrible. Have you even attempted to implement any nontrivial algorithm in it? Have you simulated it? Implementing a synthesizable Verilog/VHDL simulation would be educational for you.

Name: Anonymous 2011-09-22 9:54

>>3
I'm pretty sure that I'll be missing some important instructions, so a few more bits of opcode headroom is a good idea. This is my first venture into instruction set design, after briefly studying the x86 and PowerPC instruction sets. Do you know of any 'holes' in my instruction set? For example, I saw a 'system call' instruction, mnemonic sc, in PowerPC. I don't understand how that works at all, but I can gather that it's fairly important.

Name: Anonymous 2011-09-22 9:55

>>4

Fucking terrible.
As I would expect, as this is my first design, and I know next to nothing about how things really work.

Have you even attempted to implement any nontrivial algorithm in it? Have you simulated it? Implementing a synthesizable Verilog/VHDL simulation would be educational for you.
That is a good idea.

Name: Anonymous 2011-09-22 10:00

>>4
Care to explain why it's ``fucking terrible''? I don't disagree with you, but more detail and elaboration would be appreciated.

Name: Anonymous 2011-09-22 10:10

>>5
Depends on what you plan on using the instruction set for. syscall instructions are usually used for dealing with privilege levels (security) and providing a simple way of calling kernel or hypervisor code.
I would also like an indirect jump (to a register) instruction.

Name: Anonymous 2011-09-22 10:13

>>8
The j instruction is an 'indirect' jump, to a register, as you describe. The address field in that instruction is a register reference, from which the address value is pulled.

Name: Anonymous 2011-09-22 10:30

>>5

linux on the intel uses an interrupt with a value of 80, I think. It provides user programs with a method for invoking services from the operating system, like reading and writing to files, opening a file, and the like. Using the interrupt, the user program triggers the interrupt with the value of 80, and then I think the interrupt stops everything running, backs up the state of the processor, goes to a table of code pointers for handling interrupts and executes the 80'th one, which is the system call handler. Then the system call handler looks are the values in the registers, and executes an appropriate service as the operating system. When it is over, the interrupt ends and the processor is restored to its previous state. I think return values are passed in the registers. I would have to double check though. It has been a while.

Name: Anonymous 2011-09-22 11:56

run ECC on my doubles

Name: Anonymous 2011-09-22 12:16

So I write s 0, 0, 255 to write out 2^255 bytes to memory. The processor takes an exception halfway through. How does it resume execution after the exception handler completes?

Name: Anonymous 2011-09-22 12:34

write out 2^255 bytes to memory. The processor takes an exception halfway through.
Considering how "halfway through" would occur long after the heat death of the universe, I doubt it will make any difference.

Name: Anonymous 2011-09-22 13:22

Anonix quality

Name: Anonymous 2011-09-22 13:27

>>14
Anonix is not bloated unlike this ISA.

Name: Anonymous 2011-09-22 22:09

>>12
In 64-bit mode, only the values 0, 1, 2 and 3 are allowed.

Name: Anonymous 2011-09-22 22:33

hey OP/all

I also have pretty minimal knowledge in this field,
but was wondering if a Variable instruction set would be possible?

Name: Anonymous 2011-09-22 23:25

>>17
What does that even mean?

Name: Anonymous 2011-09-22 23:56

Hmm yeah good question..

Could you compress instructions?
Eg while app X is running, cpu frequently gets instructions A followed by B, C, etc... so instruct 'a' -> A + B + C...?

Name: Anonymous 2011-09-23 0:19

...might make little difference?

Programmable instruction sets then?? ...build your own SSE-n?

Name: Anonymous 2011-09-23 0:47

>>19
isn't that the CPU's job?

Name: Anonymous 2011-09-23 2:42

..instructions relate to specific circuits / components in a CPU..?
eg logic & arithmatic -> ALU... (any others?)

So, Instructions are hard-wired ?? // No such thing as a general-purpose instruction circuit =(

Probably code short circuits n shit anyway i s'pose..

Name: Anonymous 2011-09-23 7:03

>>22
There are CPUs that incorporate a degree of configurability, and there's always FPGAs, but those will always be slower and use more power than their hard-wired counterparts.

Name: Anonymous 2011-09-23 8:01

Hello again, /prog/.

After some rethinking, I've redesigned the instruction set architecture, this time with various changes, including

- instructions are now 16 bits long
- the opcode field is six bits long
- the register reference fields are five bits long
- for simplicity, ops like `A = B op C' are now `A = A op B'
- there is now a status register, currently only used for c/j
- none of that `[size]' bullshit anymore in the load/store/move ops

Overall, a hopefully cleaner and better designed instruction set.

http://jsbin.com/ekuwap/2

Any comments on this one?

Name: Anonymous 2011-09-23 8:15

Lacks SIMD.

Name: Anonymous 2011-09-23 8:21

>>25
Adding SIMD instructions, let alone any instruction that can be completed equally with a combination of other instructions, will defeat the idea of this architecture being RISC.

Name: Anonymous 2011-09-23 8:44

And now for an (untested) emulator in ~50 lines.

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define MEM 67108864
#define BITS 64
#define uw uint64_t
#define sw int64_t
int main() {
    uint8_t *mem = calloc(MEM, 1);
    uint16_t *memi = (uint16_t *) mem, ins, insd, insa, insb;
    uint32_t stat = 0;
    uw *reg = calloc(32, BITS / 8), cia = 0, nia;
    while (1) {
        nia = cia + 1;
        ins = memi[cia];
        insd = ins & 0x3ff;
        insa = insd >> 5;
        insb = insd & 0x1f;
        switch (ins >> 10) {
            case 0: break;
            case 1: mem[reg[insa]] = mem[reg[insb]]; break;
            case 2: reg[insa] = ((reg[insa] >> 8) << 8) | mem[reg[insb]]; break;
            case 3: mem[reg[insa]] = reg[insb]; break;
            case 4: reg[insa] = (insb >> 1) << ((insb & 1) ? 4 : 0); break;
            case 5: reg[insa] = ~reg[insa]; break;
            case 6: reg[insa] &= reg[insb]; break;
            case 7: reg[insa] |= reg[insb]; break;
            case 8: reg[insa] ^= reg[insb]; break;
            case 9: reg[insa] += reg[insb]; break;
            case 10: reg[insa] -= reg[insb]; break;
            case 11: reg[insa] <<= reg[insb]; break;
            case 12: reg[insa] >>= reg[insb]; break;
            case 13: reg[insa] = (uw) (((sw) (reg[insa])) >> reg[insb]); break;
            case 14:
                stat = (stat >> 3) << 3;
                if (reg[insa] == reg[insb])
                    stat |= 1;
                else if (reg[insa] < reg[insb])
                    stat |= 2;
                else
                    stat |= 4;
            case 15:
                if (((insb >> 1) & 7) & (stat & 7))
                    nia = reg[insa] + ((insb & 1) ? cia : 0);
        }
        cia = nia;
        if (cia > MEM / 2 - 1)
            break;
    }
    free(mem);
    free(reg);
    return 0;
}

Name: Anonymous 2011-09-23 8:47

>>4
Fucking terrible.
* Fucking Terrible!

Name: Anonymous 2011-09-23 8:58

With fixed `i' and op-by-op debugging:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define MEM 67108864
#define BITS 64
#define uw uint64_t
#define sw int64_t
int main() {
    uint8_t *mem = calloc(MEM, 1);
    uint16_t *memi = (uint16_t *) mem, ins, insd, insa, insb;
    uint32_t stat = 0;
    uw *reg = calloc(32, BITS / 8), cia = 0, nia;
    memi[0] = (4 << 10) | (0 << 5) | (0x1 << 1) | 1;
    memi[1] = (4 << 10) | (0 << 5) | (0x2 << 1) | 0;
    while (1) {
        nia = cia + 1;
        ins = memi[cia];
        insd = ins & 0x3ff;
        insa = insd >> 5;
        insb = insd & 0x1f;
        switch (ins >> 10) {
            case 0: break;
            case 1: mem[reg[insa]] = mem[reg[insb]]; break;
            case 2: reg[insa] = ((reg[insa] >> 8) << 8) | mem[reg[insb]]; break;
            case 3: mem[reg[insa]] = reg[insb]; break;
            case 4:
                if (insb & 1)
                    reg[insa] = ((reg[insa] >> 8) << 8) |
                        (reg[insa] & 0xf) | ((insb >> 1) << 4);
                else
                    reg[insa] = ((reg[insa] >> 4) << 4) |
                        (insb >> 1);
                break;
            case 5: reg[insa] = ~reg[insa]; break;
            case 6: reg[insa] &= reg[insb]; break;
            case 7: reg[insa] |= reg[insb]; break;
            case 8: reg[insa] ^= reg[insb]; break;
            case 9: reg[insa] += reg[insb]; break;
            case 10: reg[insa] -= reg[insb]; break;
            case 11: reg[insa] <<= reg[insb]; break;
            case 12: reg[insa] >>= reg[insb]; break;
            case 13: reg[insa] = (uw) (((sw) (reg[insa])) >> reg[insb]); break;
            case 14:
                stat = (stat >> 3) << 3;
                if (reg[insa] == reg[insb])
                    stat |= 1;
                else if (reg[insa] < reg[insb])
                    stat |= 2;
                else
                    stat |= 4;
            case 15:
                if (((insb >> 1) & 7) & (stat & 7))
                    nia = reg[insa] + ((insb & 1) ? cia : 0);
        }
        if (ins) {
            putchar('[');
            putchar(((ins >> 15) & 1) ? '1' : '0');
            putchar(((ins >> 14) & 1) ? '1' : '0');
            putchar(((ins >> 13) & 1) ? '1' : '0');
            putchar(((ins >> 12) & 1) ? '1' : '0');
            putchar(((ins >> 11) & 1) ? '1' : '0');
            putchar(((ins >> 10) & 1) ? '1' : '0');
            putchar(((ins >> 9) & 1) ? '1' : '0');
            putchar(((ins >> 8) & 1) ? '1' : '0');
            putchar(((ins >> 7) & 1) ? '1' : '0');
            putchar(((ins >> 6) & 1) ? '1' : '0');
            putchar(((ins >> 5) & 1) ? '1' : '0');
            putchar(((ins >> 4) & 1) ? '1' : '0');
            putchar(((ins >> 3) & 1) ? '1' : '0');
            putchar(((ins >> 2) & 1) ? '1' : '0');
            putchar(((ins >> 1) & 1) ? '1' : '0');
            putchar((ins & 1) ? '1' : '0');
            putchar(']');
            {
                int i;
                for (i = 0; i < 32; i++)
                    printf(" %lx", reg[i]);
            }
            putchar('\n');
        }
        cia = nia;
        if (cia > MEM / 2 - 1)
            break;
    }
    free(mem);
    free(reg);
    return 0;
}

Name: Anonymous 2011-09-23 9:06

Fact

It takes 12 instructions to load 0x12345678 into the first register.

Process

0. load the value 0x8 into the second register
1. load the value 0x1 into the high half of the lowest byte of the first register
2. load the value 0x2 into the low half of the lowest byte of the first register
3. shift the first register left by the second register (8)
4. load the value 0x3 into the high half of the lowest byte of the first register
5. load the value 0x4 into the low half of the lowest byte of the first register
6. shift the first register left by the second register (8)
7. load the value 0x5 into the high half of the lowest byte of the first register
8. load the value 0x6 into the low half of the lowest byte of the first register
9. shift the first register left by the second register (8)
10. load the value 0x7 into the high half of the lowest byte of the first register
11. load the value 0x8 into the low half of the lowest byte of the first register

Debugging output

[0001000000110000] 0 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0001000000000011] 10 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0001000000000100] 12 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0010110000000001] 1200 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0001000000000111] 1230 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0001000000001000] 1234 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0010110000000001] 123400 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0001000000001011] 123450 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0001000000001100] 123456 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0010110000000001] 12345600 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0001000000001111] 12345670 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[0001000000010000] 12345678 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0


Source code

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define MEM 67108864
#define BITS 64
#define uw uint64_t
#define sw int64_t
int main() {
    uint8_t *mem = calloc(MEM, 1);
    uint16_t *memi = (uint16_t *) mem, ins, insd, insa, insb;
    uint32_t stat = 0;
    uw *reg = calloc(32, BITS / 8), cia = 0, nia;
    memi[0] = (4 << 10) | (1 << 5) | (0x8 << 1) | 0;
    memi[1] = (4 << 10) | (0 << 5) | (0x1 << 1) | 1;
    memi[2] = (4 << 10) | (0 << 5) | (0x2 << 1) | 0;
    memi[3] = (11 << 10) | (0 << 5) | 1;
    memi[4] = (4 << 10) | (0 << 5) | (0x3 << 1) | 1;
    memi[5] = (4 << 10) | (0 << 5) | (0x4 << 1) | 0;
    memi[6] = (11 << 10) | (0 << 5) | 1;
    memi[7] = (4 << 10) | (0 << 5) | (0x5 << 1) | 1;
    memi[8] = (4 << 10) | (0 << 5) | (0x6 << 1) | 0;
    memi[9] = (11 << 10) | (0 << 5) | 1;
    memi[10] = (4 << 10) | (0 << 5) | (0x7 << 1) | 1;
    memi[11] = (4 << 10) | (0 << 5) | (0x8 << 1) | 0;
    while (1) {
        nia = cia + 1;
        ins = memi[cia];
        insd = ins & 0x3ff;
        insa = insd >> 5;
        insb = insd & 0x1f;
        switch (ins >> 10) {
            case 0: break;
            case 1: mem[reg[insa]] = mem[reg[insb]]; break;
            case 2: reg[insa] = ((reg[insa] >> 8) << 8) | mem[reg[insb]]; break;
            case 3: mem[reg[insa]] = reg[insb]; break;
            case 4:
                if (insb & 1)
                    reg[insa] = ((reg[insa] >> 8) << 8) |
                        (reg[insa] & 0xf) | ((insb >> 1) << 4);
                else
                    reg[insa] = ((reg[insa] >> 4) << 4) |
                        (insb >> 1);
                break;
            case 5: reg[insa] = ~reg[insa]; break;
            case 6: reg[insa] &= reg[insb]; break;
            case 7: reg[insa] |= reg[insb]; break;
            case 8: reg[insa] ^= reg[insb]; break;
            case 9: reg[insa] += reg[insb]; break;
            case 10: reg[insa] -= reg[insb]; break;
            case 11: reg[insa] <<= reg[insb]; break;
            case 12: reg[insa] >>= reg[insb]; break;
            case 13: reg[insa] = (uw) (((sw) (reg[insa])) >> reg[insb]); break;
            case 14:
                stat = (stat >> 3) << 3;
                if (reg[insa] == reg[insb])
                    stat |= 1;
                else if (reg[insa] < reg[insb])
                    stat |= 2;
                else
                    stat |= 4;
            case 15:
                if (((insb >> 1) & 7) & (stat & 7))
                    nia = reg[insa] + ((insb & 1) ? cia : 0);
        }
        if (ins) {
            putchar('[');
            putchar(((ins >> 15) & 1) ? '1' : '0');
            putchar(((ins >> 14) & 1) ? '1' : '0');
            putchar(((ins >> 13) & 1) ? '1' : '0');
            putchar(((ins >> 12) & 1) ? '1' : '0');
            putchar(((ins >> 11) & 1) ? '1' : '0');
            putchar(((ins >> 10) & 1) ? '1' : '0');
            putchar(((ins >> 9) & 1) ? '1' : '0');
            putchar(((ins >> 8) & 1) ? '1' : '0');
            putchar(((ins >> 7) & 1) ? '1' : '0');
            putchar(((ins >> 6) & 1) ? '1' : '0');
            putchar(((ins >> 5) & 1) ? '1' : '0');
            putchar(((ins >> 4) & 1) ? '1' : '0');
            putchar(((ins >> 3) & 1) ? '1' : '0');
            putchar(((ins >> 2) & 1) ? '1' : '0');
            putchar(((ins >> 1) & 1) ? '1' : '0');
            putchar((ins & 1) ? '1' : '0');
            putchar(']');
            {
                int i;
                for (i = 0; i < 32; i++)
                    printf(" %lx", reg[i]);
            }
            putchar('\n');
        }
        cia = nia;
        if (cia > MEM / 2 - 1)
            break;
    }
    free(mem);
    free(reg);
    return 0;
}

Name: Anonymous 2011-09-23 12:59

Reminds me of ARM Thumb.

Name: Anonymous 2011-09-23 13:38

Name: Anonymous 2011-09-23 15:41

>>30
Fucking Terrible!

You can't even copy the value of one register into another. Instead, you have to write the first register out to memory one byte at a time, read the data back into the register one byte at a time and then read the data into the target register one byte at a time.

Also, no way of detecting overflow/underflow in arithmetic operations, or perform bit rotation etc. etc.

>>31
Except Thumb was designed by people who had a fucking clue.

Name: Anonymous 2011-09-23 21:22

>>33

You can't even copy the value of one register into another. Instead, you have to write the first register out to memory one byte at a time, read the data back into the register one byte at a time and then read the data into the target register one byte at a time.
Oh, shit. That's a gaping implementation hole.

Also, no way of detecting overflow/underflow in arithmetic operations, or perform bit rotation etc. etc.
Those are good ideas. How common are their use? Should they be included?

Name: Anonymous 2011-09-23 22:21

>>34
Go download and read through a bunch of CPU datasheets for as many architectures you can find. That should give you an idea of what practical instruction sets look like.

Name: Anonymous 2011-09-23 22:30

>>35
Wouldn't I just end up creating a large instruction set like the rest? Even PowerPC's ``RISC'' ISA seems very large.

Name: Anonymous 2011-09-23 22:50

Sigh.

Name: Anonymous 2011-09-23 22:51

>>34
As I said earlier, try implementing some nontrivial algorithms and you'll notice what's missing and what's just badly designed.

Also, the goal of RISC is to have simple instructions that can be executed quickly and without micro-code. Having a small number of instructions is a fallacy.

Have you read your Hennessy & Patterson today?

Name: Anonymous 2011-09-23 23:17



//---------
putchar(((ins >> 2) & 1) ? '1' : '0');
//---------

Vs ?

Const char BinString{"01"};
...
putchar( BinStr[ ((ins & 4)==4) ] );


Any faster..?

Name: Anonymous 2011-09-24 0:03

**
const char[2] BinString{"01"};
**

Comparison op should be faster than a rotate?
and a lookup faster than a branch?

...then putchar probably dulls it all down anyway =)

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List