Opcodes ISA --- De onde eles vêm?

13

Quando os engenheiros estão projetando uma arquitetura de conjunto de instruções, por qual procedimento ou protocolo, se houver, eles seguem ao designar determinados códigos binários como instruções. Por exemplo, se eu tenho um ISA que diz 10110 é uma instrução de carga, de onde veio esse número binário? Foi modelado a partir de uma tabela de estados para uma máquina de estados finitos representando uma operação de carregamento?

Edit: Depois de fazer mais pesquisas, acredito que o que estou tentando perguntar se preocupa como os códigos de operação das várias instruções da CPU são atribuídos. ADD pode ser designado com um código de operação 10011; uma instrução de carga pode ser designada como 10110. Qual é o processo de pensamento para atribuir esses códigos opcionais binários ao conjunto de instruções?

Steven
fonte
8
O Monte Dalrymple, "Projeto de microprocessador usando HDL Verilog", fornece uma abordagem de projeto muito detalhada para a CPU Z80 e, a partir dela, acho que você aprenderia muito sobre sua pergunta. Mas existem muitas considerações que fazem uma escolha específica, incluindo análise estatística de outros conjuntos de instruções, saídas do compilador, etc. Porém, eu recomendo começar com esse livro. Embora comece com um design conhecido, ele entra em detalhes íntimos e acho que você entenderia algumas coisas. Bom livro.
jonk
Ou, talvez, você esteja perguntando sobre o design do mecanismo de execução e se perguntando como os bits da instrução podem se encaixar nisso? Não tenho certeza do seu texto.
jonk
2
Alguém mais faz essa pergunta. Deve ser terça-feira.
Ignacio Vazquez-Abrams
5
@ Steven Pense sobre isso. Se você tivesse que projetar um ISA, o que pensaria? Se suas instruções não tivessem o mesmo comprimento, como você escolheria palavras de instrução mais curtas ou mais longas, para quais instruções? Se você tivesse que projetar um estágio de decodificação , como você gostaria que o seu ISA fosse ? Eu acho que a pergunta é desnecessariamente ampla (e, portanto, quase impossível de responder completamente), mas você pode aprimorá-la muito colocando mais um pensamento próprio e fazendo uma pergunta precisa que não exigiria que escrevêssemos um livro para responder isto.
Marcus Müller
4
As especificações do RISC-V falam sobre as decisões de projeto que eles tomaram em todos os níveis, incluindo bastante sobre a codificação das instruções da máquina. (Este é incomum para um processador manual; RISC-V é um exercício académico primeira e uma segunda arquitectura da CPU, ao contrário da maioria.)
Zwol

Respostas:

6

Em muitos casos, a escolha é bastante arbitrária ou baseada em "onde for melhor", à medida que os ISAs crescem com o tempo. No entanto, o MOS 6502 é um exemplo maravilhoso de chip em que o design do ISA foi fortemente influenciado pela tentativa de extrair o máximo possível de transistores limitados.

Confira este vídeo explicando como o 6502 foi modificado com engenharia reversa , principalmente a partir das 34:20.

O 6502 é um microprocessador de 8 bits lançado em 1975. Embora tivesse 60% menos portas do que o Z80, era duas vezes mais rápido e, embora fosse mais restrito (em termos de registros etc.), compensava isso com um conjunto de instruções elegante.

Ele contém apenas 3510 transistores, que foram desenhados à mão por uma pequena equipe de pessoas rastejando sobre grandes folhas de plástico que foram posteriormente reduzidas opticamente, formando as várias camadas do 6502.

Como você pode ver abaixo, o 6502 passa o código de operação da instrução e os dados de temporização para a ROM de decodificação e os passa para um componente de "lógica de controle aleatório", cujo objetivo provavelmente é anular a saída da ROM em determinadas situações complexas.

6502 diagrama de blocos

Às 37:00 do vídeo, você pode ver uma tabela da ROM de decodificação que mostra quais condições as entradas devem atender para obter um "1" para uma determinada saída de controle. Você também pode encontrá-lo nesta página .

Você pode ver que a maioria das coisas nesta tabela tem Xs em várias posições. Vamos pegar, por exemplo

011XXXXX 2 X RORRORA

Isso significa que os 3 primeiros bits do código de operação devem ser 011 e G deve ser 2; nada mais importa. Nesse caso, a saída denominada RORRORA será verdadeira. Todos os opcodes ROR começam com 011; mas há outras instruções que começam com 011 também. Provavelmente, eles precisam ser filtrados pela unidade "lógica de controle aleatório".

Então, basicamente, os opcodes foram escolhidos para que as instruções que precisavam fazer a mesma coisa tivessem algo em comum em seu padrão de bits. Você pode ver isso olhando para uma tabela opcode ; todas as instruções OR começam com 000, todas as instruções da loja começam com 010, todas as instruções que usam o endereçamento de página zero têm o formato xxxx01xx. Obviamente, algumas instruções parecem não se encaixar, porque o objetivo não é ter um formato de código de operação completamente regular, mas fornecer um conjunto de instruções poderoso. E é por isso que a "lógica de controle aleatório" era necessária.

A página que mencionei acima diz que algumas das linhas de saída na ROM aparecem duas vezes: "Assumimos que isso tenha sido feito porque eles não tinham como rotear a saída de alguma linha para onde queriam, então colocaram a mesma linha em um local diferente. localização novamente. " Posso imaginar os engenheiros desenhando à mão esses portões um a um e subitamente percebendo uma falha no design e tentando encontrar uma maneira de evitar o reinício de todo o processo.

Artelius
fonte
22

Depende da idade do ISA.

Nos primórdios do design manual, e ainda mais quando as CPUs eram montadas a partir de lógica discreta, o design lógico teria sido o primeiro e seria amplamente minimizado, e então os padrões de bits ISA teriam sido os valores necessários para tornar o mínimo trabalho lógico.

Portanto, pode haver um padrão específico de sinais de controle que permita que alguns multiplexadores conectem a saída da ALU à entrada do arquivo de registro GP, mais alguns sinais de controle que instruem a ALU a adicionar, subtrair, AND, OR etc. e alguns bits de endereço no arquivo de registro. Esses três grupos de sinais formarão campos dentro da instrução. Cada grupo será mantido junto, e seu significado detalhado surge do design dessa unidade (ALU etc.), mas os grupos podem estar em qualquer ordem, até você projetar o decodificador de instruções. (o x86 tem idade suficiente para detectar um pouco disso, se você olhar no lugar certo - não era um design totalmente novo, mas foi extraído do antigo 8080)

Os ISAs posteriores podem ser "limpos" e tornados mais regulares e mais simples de usar, com hardware para traduzir entre eles e os sinais de controle reais no nível do hardware, às vezes via "microcódigo". Eles são chamados de "CISC" ou "Código de conjunto de instruções complexo". O prefixo da instrução "Rep" x86 é um exemplo simples disso - faz com que a instrução a seguir seja repetida várias vezes, para economizar a necessidade de gravar um loop FOR.

Mais tarde ainda (nos anos 80), voltou-se para um estilo mais simples de codificação direta (RISC - Reduced Instruction Set Coding), que você pode ver nos processadores ARM. Isso foi motivado pelo pequeno tamanho dos ASICs na época e pelo desejo de colocar CPUs de 32 bits neles, portanto, não havia capacidade disponível para decodificadores complexos de conjuntos de instruções, para reduzir o CPU completo para cerca de 20.000 portas. (Houve também um aumento temporário no desempenho, porque as pessoas ainda não haviam desenvolvido técnicas para acelerar os decodificadores CISC - que ocorreram em 1995 com o Pentium Pro)

E hoje em dia não importa - as CPUs leem várias instruções de uma só vez e dedicam milhões de transistores a decodificá-las, reordená-las e executar o maior número possível de uma só vez, para acelerar programas que podem ter sido escritos para os mais antigos estilo do ISA.

Brian Drummond
fonte
2
Não tenho certeza se chamaria o CISC de "mais fácil de usar". Essa pode ter sido a intenção original, mas 30 anos depois eles são meio que a antítese do "fácil de usar" (em comparação com os ISA RISC, pelo menos).
tonysdg 5/09/17
2
Há aspectos em que eles eram mais fáceis de usar ... ou a regularidade (ortogonalidade era um grande tópico) quando os compiladores eram programas relativamente triviais ou através do suporte direto a operações de nível superior, exigindo menos tradução do compilador. Mas isso foi há muito tempo e qualquer CISC sobrevivente tem tantas camadas de revisões além de seu conjunto de instruções original. Os compiladores também mudaram de todo o reconhecimento - os cerca de mil passes de otimização executados pelo gcc seriam impensáveis ​​naquela época. Então, o que era "fácil" naquela época e agora tem muito pouca relação.
Brian Drummond
4
A distinção foi corroída (conjuntos "RISC" adicionando mais instruções) e substituída por novas arquiteturas ainda mais complexas, como o VLIW; realmente o único consenso é que x86 (16 e 32 bits) é difícil de usar
pjc50
1
@tonysdg: Existem RISC difíceis de usar e CISC difíceis de usar. Uma boa comparação de "facilidade de programação" é comparar 68k vs ARM. O ARM foi projetado para um compilador, portanto você precisava fazer muito trabalho manual para obter dados da RAM e gravá-los novamente. O 68k foi projetado para programadores de montagem e permite operar diretamente em dados na RAM. Se você observar o ISA de 68k, descobrirá que ele se parece muito com o ISA RISC moderno, com uma exceção - você pode operar diretamente na RAM, enquanto o RISC permite apenas operar com registros.
slebetman 6/09/17
1
Microcódigo é principalmente um atributo CISC. No entanto, você pode implementar o CISC sem microcódigo: o decodificador de instruções seria mais complicado. Você também verá alguns CISCs do Pentium-Pro em diante descritos como RISC internamente; traduzir cada instrução CISC em uma ou mais internas ops RISC: outro nome para microcódigo (embora as distinções se turva em unidades de execução superescalares)
Brian Drummond
9

Se você agrupar instruções semelhantes, surgirão padrões. Isso é muito óbvio no ARM, onde o manual ISA realmente mostra qual parte da palavra de instrução corresponde à função, escolha de registro, etc. Mas também pode ser inferida para o X86 .

Por fim, a parte "função" dos códigos de operação entra em algum decodificador binário-para-um que realmente ativa uma função ou sequência específica de operações em pipeline. Geralmente, eles não estão relacionados ao conteúdo de nenhuma máquina de estado, a menos que consideremos instruções de comprimento variável que exigem a decodificação de uma máquina de estado.

pjc50
fonte
Você está basicamente dizendo que eles estão buscando a menor contagem possível de transistores no chip. Eu concordo totalmente no contexto da pergunta do OP, onde eles não podem pagar centenas de transistores extras para um conjunto de instruções mais organizado. As CPUs de um milhão de transistores não têm tanta razão para se preocupar, mas é claro que muitas a retêm para compatibilidade com versões anteriores.
Harper - Restabelece Monica
@ Harper Ainda há motivos, porque, embora os transistores tenham diminuído, eles ainda têm um tamanho - e as taxas de clock aumentaram muito nesse meio tempo. Portanto, um decodificador de instruções muito grande ainda pode ser um gargalo para o desempenho (uma das razões pelas quais muitas CPUs optaram por pré- decodificar instruções com antecedência). Não se trata (apenas) da contagem de transistores, mas mais da taxa de clock em combinação com a área da matriz. As informações ainda levam tempo para se propagar e, embora as CPUs modernas não estejam funcionando na velocidade da luz, elas não estão longe o suficiente do limite de velocidade para esperar melhorias significativas.
Luaan 6/09/17
@Luaan: Na verdade, "o que fazemos com todos esses transistores" é uma questão real hoje em dia. Veja todos os caches L2 / L3 lançados hoje em dia. Essa é uma admissão silenciosa que não temos um uso melhor para todos esses milhões de transistores. Os últimos Xeon's dedicam mais de 2 bilhões de transistores ao cache!
precisa saber é o seguinte
6

Alguém em algum momento sentou-se e os definiu.

Um bom ISA tornará o decodificador o mais simples possível.

Por exemplo, com uma instrução ALU, você pode permitir que alguns bits do código op sejam enviados diretamente para as linhas de controle da ALU.

catraca arrepiante
fonte
Obrigado a todos pelas excelentes respostas. Todos vocês me ajudaram a entender isso muito melhor.
Steven
4
Na verdade, existem alguns fatores além da simplicidade do decodificador a serem considerados. Dependendo das circunstâncias e do uso pretendido, outros (por exemplo, densidade de código) podem ser mais importantes que a simplicidade do decodificador. Em um processador moderno, a densidade do código provavelmente supera a simplicidade do decodificador na maioria dos casos.
Jerry Coffin
5

Normalmente, você dividiria seu ISA em grupos funcionais. Faz sentido (para otimização lógica ou apenas para ser organizado) que os pares complementares sejam diferenciados por uma única alteração de bit (carga versus armazenamento) e que você tenha alguma hierarquia de bits que afeta a árvore de decisão de decodificação.

No final do dia, uma alocação arbitrária de bits para o bloco de funções (em vez de colocar os campos "dados" na instrução terá apenas um pequeno impacto na eficiência geral do projeto - mas você tem muitas opções sobre como 'otimize' sua codificação ISA, dependendo do que você considera um parâmetro importante.

Sean Houlihane
fonte
1

A codificação de instruções é um compromisso feio entre.

Simplificando a decodificação, para isso, você deseja um conjunto simples de campos, cada um dos quais pode ser decodificado separadamente e roteado para uma parte separada do mecanismo de execução.

Empacotar o máximo de funcionalidade possível em um tamanho limitado da palavra de instrução. Isso leva a coisas como formatos constantes especiais que podem codificar uma variedade de números comuns.

Compatibilidade para frente e para trás. Se você atribuir funcionalidade a todo código de operação possível, não terá espaço para expandir a arquitetura posteriormente. Se você estiver adicionando a uma arquitetura existente, precisará inserir suas novas instruções nos códigos de operação sobressalentes.

Peter Green
fonte
1

A excelente arte de montagem de Randy Hyde entra em detalhes sobre o conjunto de instruções x86 no capítulo 3.3.4 A unidade de controle e os conjuntos de instruções e seguintes.

Os programas nos primeiros sistemas de computadores (pré-Von Neumann) eram frequentemente "conectados" nos circuitos. Ou seja, a fiação do computador determinava qual problema o computador iria resolver. Foi necessário religar o circuito para mudar o programa. Uma tarefa muito difícil. O próximo avanço no design do computador foi o sistema de computador programável, que permitia que um programador de computador "religasse" facilmente o sistema usando uma sequência de soquetes e fios. Um programa de computador consistia em um conjunto de linhas de orifícios (soquetes), cada linha representando uma operação durante a execução do programa. O programador pode selecionar uma das várias instruções conectando um fio ao soquete específico para a instrução desejada.

Ele então demonstra bastante cativante e detalhadamente como o primeiro par de plugues representa a instrução, os próximos plugs codificam a origem e o destino. É claro que hoje ninguém mais "se conecta", mas para os ISA realmente antigos, os bits no código de operação basicamente fazem o mesmo trabalho que os plugs anteriores.

Você acaba com algo assim:

insira a descrição da imagem aqui

DevSolar
fonte
Obrigado pelo link da Hyde! É muito informativo e ele parece ter um excelente estilo de ensino.
Steven