Para a questão da divisão e TI

"Não se mostre, Maria Ivanovna, e ouça sua música favorita," Valenki "



Apesar da manchete, a ordem da apresentação será o oposto - primeiro sobre a Texas Instruments (é claro que não sobre a própria empresa, ainda sou engenheiro, não sou treinador, analista de negócios e, portanto, sobre os produtos fabricados pela empresa), e só depois sobre a divisão.

A primeira parte do balé Marlezon.

O assunto em discussão será a relativamente nova família SS13xx (SS1310 / SS1350 / SS1352), mas é apenas um ponto de partida para discutir a situação no campo da programação de sistemas embarcados. Este MK é destinado ao uso no design de dispositivos com interfaces sem fio de vários tipos (eu realmente não gosto da palavra IoT, especialmente porque não esgota a possibilidade de usar essa família).

O MK é construído com base no núcleo M3, com parâmetros completamente aceitáveis, embora sem quebrar recordes, para a frequência e tamanho da memória de programas e dados; possui um conjunto suficiente de interfaces, mas esses parâmetros não são de forma alguma interessantes. A característica é que o microcircuito contém três núcleos MK, um central e dois periféricos, para trabalhar com dispositivos externos e para interagir via éter. Qual o motivo desta decisão?

Primeiro de tudo, o desejo de garantir o mínimo consumo de energia. O fato é que a maneira tradicional de reduzir o consumo realizando cálculos com uso intensivo de recursos em um tempo relativamente curto e, em seguida, alternando para o modo de espera com uma diminuição na frequência do relógio tem limitações naturais e um núcleo rápido consumirá relativamente a uma frequência mais baixa, sem fornecer o tempo de reação necessário para eventos externos. Para eliminar essa contradição, foi introduzido o segundo núcleo do controlador do sensor, que fornece interação com o mundo externo no modo de baixo consumo de energia e, quando surge a necessidade de cálculos com muitos recursos, ele ativa o núcleo principal.

O terceiro controlador resolve (embora de uma maneira muito complicada) a mesma tarefa de reduzir o consumo. O fato é que a manutenção de protocolos de troca de rádio geralmente exige a manutenção de restrições de tempo muito rigorosas e uma tentativa de implementá-los no núcleo central (ao mesmo tempo em que executa o programa de destino) exigirá o aumento da frequência do relógio do núcleo e, consequentemente, da energia consumida pela fonte de energia. A separação de funções permite remover essa contradição (uma solução típica no estilo TRIZ).

Não tenho certeza absoluta de que essa separação de funções seja absolutamente necessária e que os parâmetros necessários não possam ser alcançados com soluções arquiteturais mais simples, mas se o preço for mantido em uma estrutura razoável e os recursos necessários forem realizados, por que não? Além disso, o post ainda não é sobre o componente de hardware do MK, é apenas para cobrir a situação. Vamos considerar o processo de criação de software para essa classe de MK.

Para começar - o núcleo principal, tudo é padrão aqui - o núcleo M3 é bem conhecido e o conjunto de ferramentas é gcc, a própria empresa recomenda dois IDEs - ccs e iar. Eu trabalhei muito com o último deles, então decidi experimentar que tipo de produto o sombrio gênio teutônico da mente coletiva que a TI gerava. O Code Composer Studio é um desenvolvimento da empresa e é totalmente gratuito, sem restrições, com base no (quem pensaria) no Eclipse e possui todas as vantagens e desvantagens inerentes a esse ambiente.

A única coisa que gostaria de expressar de imediato minha perplexidade é que a empresa gentilmente oferece utilitários adicionais para trabalhar com este MK (para criar uma imagem de firmware inicializável via éter e para transferi-la para MK), mas eles são escritos, por algum motivo, não em Java, que é a base para o ambiente de programação e o sistema de tempo de execução do qual faz parte do pacote de instalação e no Phyton. Não é que eu não gostei muito do último (embora exista um, não aceito definir a estrutura do programa com indentação, mas no final "as cores e as cores são diferentes"), mas não está claro por que é necessário atrair entidades obviamente redundantes. Além disso, os utilitários em si não são nada complicados, eles não usam bibliotecas específicas e foram transferidos pelo autor para Java por um tempo muito limitado, sem a menor dificuldade em aumentar o comprimento do programa em 20% e, o que é um pouco estranho, sem alterações visíveis na velocidade (considerando que os principais o tempo de execução está associado à leitura de arquivos, isso não é tão surpreendente - uma observação tardia de Funtik F ...).

A segunda parte do quebra-cabeça está no fato de que o próprio Eclipse é popular devido à capacidade de incorporar plugins facilmente. Dado esse fato, a decisão dos desenvolvedores do ambiente de programação de fazer o usuário chamar o utilitário com alças a partir da linha de comando é extremamente enigmática, tendo anteriormente desabilitado o terminal no ambiente de desenvolvimento com as alças e iniciado o programa de recebimento de dados no MK com as alças e, em seguida, restaurado o terminal novamente com as alças. Talvez para “programadores hindus” essa solução pareça ser a única possível e completamente justificada, mas uma grande empresa possa se dar ao luxo de atrair pessoal mais qualificado, provavelmente não sei de nada.

Além disso, a programação do controlador do sensor (o nome não é muito bem-sucedido, mas é um papel vegetal direto) é realizada usando o produto Sensor Composer Studio. Imediatamente outra pergunta - por que é necessário ter um produto separado, não que fosse muito difícil mudar a janela, mas ainda assim ..., principalmente porque o código criado acaba se tornando parte do código do MK principal (é claro, está lá não executado, mas incluído no espaço de endereço geral da memória do programa).

E aqui está outro recurso: eles não nos dizem nada sobre esse núcleo (sua arquitetura), exceto pelo fato de ser de 16 bits, de baixo consumo e proprietário. Em geral, tudo bem, funciona e funciona bem, mas "o sedimento permanece". A seguir, é apresentada uma descrição dos comandos desse kernel, segundo a qual podemos assumir que essa é uma modificação do 430, embora com recursos como comandos para organizar ciclos. A localização dos registros periféricos no espaço de endereços geral e local é fornecida e, em seguida, a estranheza começa novamente - muitos registros, incluindo registros periféricos, são acompanhados pela frase “é usada apenas pela biblioteca da TI” e, para algumas das atribuições de campo de bits, ainda são dados, e para alguns não são. Não que essas descrições dos registros me incomodem muito, mas por que fornecê-las se o usuário não planeja usá-las - eu pessoalmente não entendo muito?

Você também pode acessar a periferia deste kernel a partir do kernel principal usando bibliotecas especiais, ao mesmo tempo em que pode escrever código no ambiente de programação mencionado, novamente usando alguns plug-ins. Tudo está bem aqui, a documentação é suficiente, as configurações são convenientes, há uma representação gráfica das configurações, depuração interna em um modo especial (no modo geral, o depurador está ocupado com o kernel principal), em geral, é bastante digno.

A próxima parte é o kernel para trabalhar com rádio, construído com base no M0, executa um programa a partir de sua própria ROM (provavelmente faz parte de memória não volátil, é improvável que um MK moderno tenha uma memória mascarada), que não pode ser modificado (pelo menos sobre isso em documentação, não uma palavra) pelo usuário. As informações sobre a estrutura interna do canal de rádio são extremamente escassas; na verdade, podem ser extraídas apenas da descrição dos comandos de configuração de modo, mas não são necessárias para o desenvolvedor "embutido" usual.

A interação entre o núcleo principal e a parte do rádio é baseada no fluxo de mensagens, documentado em detalhes suficientes. E essa documentação é suficiente para entender a coisa simples - você (bem, eu definitivamente não vou) criar seus mecanismos de interação entre os kernels e, principalmente, os protocolos de comunicação, mas você usará a biblioteca implementada pela empresa, porque a interação é bastante complicada, mesmo com ela O design deve levar em consideração um grande número de vários fatores, para que os benefícios de escrever seu próprio pacote não compensem o custo de sua criação. Portanto, usamos novamente a subcamada da biblioteca para organizar a interação do núcleo principal com o núcleo do rádio e, provavelmente, usamos soluções prontas para organizar um canal de radiocomunicação padronizado usando módulos prontos, usando apenas a parametrização do canal.

Acho que ficou claro para o leitor que escrever um programa completo para este MK não é uma tarefa simples, é bastante acessível para um profissional avançado, mas o que um desenvolvedor "comum" deve fazer (como escreveu recentemente Oleg Artamonov: "Você já percebeu que cometeu um grande erro com a escolha profissões? ”). A empresa cuidou desse caso e, juntamente com o ambiente de desenvolvimento, fornece um grande pacote (conjunto de exemplos) de programas para todas as ocasiões, chamado SimpleLink. Além disso, as soluções são fornecidas em duas versões - ambas usando o sistema operacional TI-RTOS em tempo real (na minha opinião, essa é uma maneira mais conveniente) e em um super ciclo (caso você odeie tanto o sistema operacional interno que não consiga comer "). Eu sou calmo sobre o RTOS no MK, então uso a primeira opção e aconselho você a fazer o mesmo, especialmente se você entender a configuração do processo de compilação do SO e adaptá-lo à sua classe de tarefas, a facilidade de uso será salva e o custo de manutenção dessa conveniência será drasticamente reduzido.

Minha atitude pessoal em relação a este pacote é dupla - por um lado, um empreendimento maravilhoso que simplifica bastante o uso do MK (portanto, eu o uso), mas por outro lado - “lixo, desperdício e sodomia”, uma ótima ilustração para a frase “Se o custo de uma boa arquitetura parecer É alto para você, pense no custo de uma arquitetura ruim. ”(É por isso que o repreendo.) Não se trata apenas da distribuição de funções por módulos e dos relacionamentos entre elas (embora isso não seja fácil), mas também da distribuição de módulos por arquivo, nome de arquivo, distribuição arquivos por diretórios e seus nomes e estrutura, etc. etc. Bem, violar os princípios do KISS e DRY é quase a regra para os desenvolvedores do pacote, mas se eu não entender o código-fonte do pacote (não consigo me livrar desse hábito estúpido), e usá-lo "como está", então tudo funciona bem, pelo menos não encontrei nenhum problema em um projeto específico (além de um sentimento estético insultado e de uma fé perdida na humanidade).

Mas agora você pode ir sem problemas ao postulado principal deste post (antes tarde do que nunca). Sempre achei extremamente difícil escrever uma estrutura que combine verdadeira versatilidade e alto desempenho. Os exemplos de desenvolvimento propostos pela empresa representam exatamente essa estrutura, com ênfase na versatilidade, as ferramentas de personalização de aplicativos estão quase completamente ausentes, tudo é apenas no nível de ajuste de coalhada. Pegue um de nossos muitos exemplos, altere um pequeno pedaço relacionado a medições e análises (e transmissão, é claro) e pronto. Obviamente, o código resultante será bastante inchado no caso geral, mas ofereceremos vários exemplos para diferentes condições de aplicação, e você só precisa escolher o mais adequado para sua tarefa. Em um caso extremo, uma quantidade considerável de memória de programa é incorporada ao MK, para que você não precise pensar em salvar esse recurso. Além disso, uma parte significativa das bibliotecas em execução já está oculta na ROM e você só precisa chamá-las com cuidado.

Não sou contra essa abordagem e insisti categoricamente na invenção de bicicletas, a reutilização de código é um imperativo categórico e uma garantia de alta produtividade do programador, mas sujeito às seguintes condições:
A biblioteca de rotinas usada deve ser:

  1. cuidadosamente projetado
  2. ordenadamente programado
  3. documentado de forma abrangente
  4. universal
  5. eficaz.

E se os dois últimos pontos são desejáveis ​​(altamente desejáveis, mas mesmo assim ...), os três primeiros são necessários.

E os requisitos do pacote SimpleLink oferecido pela empresa? A seguir, são apresentadas avaliações em uma escala de cinco pontos, obtidas na ordem de conhecimento superficial do pacote.

1a) As conexões entre os módulos devem ser pensadas, os limites de competência de cada módulo devem ser claramente delineados, eliminando a duplicação de funções, as interfaces elaboradas - quatro sólidas, o trabalho como um todo foi realizado.

1b) A distribuição dos módulos em arquivos com uma estrutura de diretórios bem pensada é provavelmente três com uma vantagem, que vale a pena repetir apenas o texto dos módulos do programa em arquivos diferentes.

2. O pacote não deve ter erros difíceis de detectar raramente manifestados (o fato de que ele não deve mostrar erros constantemente é óbvio). É difícil fazer estimativas aqui, observo uma circunstância desagradável - a maioria das funções pode ser chamada tanto da memória usual do programa quanto da memória permanente, e se as fontes da primeira opção forem acessíveis e validadas, a segunda será muito pior - não recebemos instruções suas origens não são idênticas à primeira opção; portanto, não há como verificar.

3. A documentação para os quatro sólidos já é o fato de que os autores não recorreram ao “poder e expressividade” do Doxygen em termos de documentação, dão um mínimo de 1 ponto, existe um sistema de links contextuais, há descrições dos princípios de funcionamento - não coloco os cinco primeiros apenas porque em termos de documentação Eu nunca defini, nem eu mesmo.

4. Não mais do que quatro devido à falta de configuração desenvolvida, mas eu já mencionei isso.

5. É difícil dizer, eu geralmente olho para a implementação do SPI - é, por um lado, simples o suficiente para apreciar a falta de rake padrão; por outro lado, é suficientemente complexo para ter onde amontoá-los. Portanto, neste pacote existem procedimentos comparativamente ineficientes para escrever / ler bytes, mas se você mergulhar nas profundezas, poderá encontrar opções realmente usadas usando o MPD, ainda não posso dizer nada sobre elas.

Uma observação sobre as profundezas - elas são realmente profundas (através de 4-5 funções aninhadas) e não posso deixar de mencionar um recurso do pacote - está escrito em C. Não fui eu que esqueci de adicionar duas vantagens depois da carta, ela realmente não está lá. Para aqueles que estão no tópico, fica muito claro, recomendo a todos que façam uma emocionante busca para determinar o conjunto de funções executadas no nível do hardware ao implementar um objeto não trivial. Obviamente, ao usar classes, essa tarefa se torna trivial, mas esse não é o caminho dos Jedi da TI. Entendo que essa é uma preocupação necessária para os usuários que negligenciam as vantagens, mas por que parar por aí, mas e os infelizes usuários do assembler, pelos quais foram ofendidos.

E, para concluir - eu quero enfatizar: “para que eu possa ser entendido corretamente no topo”, não repreendo a família MK, nem o ambiente de desenvolvimento, nem o pacote de software, apenas quero que eles se tornem ainda melhores, mais convenientes e mais atraentes para o usuário . Eu tenho minhas próprias contas com a TI e nunca as perdoarei pela aquisição da National ou pela aquisição ainda mais recente da Luminary com o assassinato subsequente de uma linha MK interessante (embora no último caso eles tenham se punido), bem como o que eles retornaram para mim em 2014 dinheiro para os cristais encomendados (embora eu definitivamente não tenha tocado na Crimeia), mas esse sentimento profundo não me impede de ser objetivo - eles fizeram um bom trabalho. O conceito proposto pela empresa, que é apresentado na epígrafe, não é muito próximo de mim, mas provavelmente está certo, e não há outro caminho para cristais complexos. Esta é uma tendência e não faz sentido combatê-la.

E o fato de que essa é uma tendência confirma a situação com os novos cristais de gerenciamento de energia da Vicor. O cristal em si é bom (o contrário seria surpreendente para uma empresa respeitável), os parâmetros são muito bons, e eu o mencionei apenas em conexão com a seção sobre a escolha de componentes externos, especificamente a indutância. Na documentação, esta seção é apenas um parágrafo, que indica um modelo específico de indutância de um fabricante específico e afirma explicitamente que outras opções não são consideradas, consulte a epígrafe. Compreender completamente as razões para o desenvolvedor do cristal (a frequência de comutação é alta, as correntes são significativas, o design da indutância para tais condições não é uma tarefa trivial), no entanto, devo observar que, por enquanto, essa abordagem de design é incomum para mim ", mas a chave aqui é" por enquanto. Talvez você devesse vender esses dois componentes em um pacote, para não haver perguntas.

A segunda parte do balé Marlezon.

Bem, agora sobre a divisão, cujo uso foi descoberto em uma das bibliotecas da TI, mas não se aplica diretamente a essa empresa, mas é um recurso do compilador gcc. Assim, formulamos o problema a partir do campo aritmético do endereço - precisamos calcular a diferença nos índices (ou seja, índices, não bytes) entre dois elementos da matriz (ou simplesmente dados sequencialmente localizados do mesmo tipo) especificados pelos ponteiros para eles.Como regra, um dos elementos é o primeiro elemento da matriz, mas isso não é importante.

A tarefa em si é simples e, em C, a solução é óbvia e parece

(pointer1pointer0)/sizeof(type)

.O diabo está escondido na implementação - a equipe da divisão não foi implementada de maneira padrão nas arquiteturas comuns do MK, por isso não é rápido. Se o divisor for uma variável, não haverá uma boa solução da palavra, mas para um valor constante, existem opções baseadas na multiplicação. Graças à maravilhosa propriedade de multiplicação

(a+b)(c+d)=ac+ad+bc+bd

(a adição é aditiva, obrigado, capitão). A implementação da multiplicação por hardware é muito mais comum em implementações e bastante rápida (o tópico sobre multiplicadores de hardware é interessante por si só, mas não é isso agora). Infelizmente, não há propriedade semelhante para a divisão, e é por isso que é um hóspede raro em termos de implementação de hardware.

Então, em vez de dividir (por constante, isso é importante), queremos aplicar a multiplicação, observe suas mãos

a/c=a(1/c)=a(N/(Nc))=a(N/c)/N

onde N é alguma constante adicional. Surge uma questão lógica - que tipo de lixo, porque agora temos duas operações de divisão em vez de uma e até multiplicação, pelas quais vencemos. A resposta é escolhida corretamente N, se for uma potência de dois, mas a divisão se transformar em um deslocamento do número para a direita, o que é muito mais barato, e se o expoente for múltiplo de 8, a divisão se transformará em uma renumeração de bytes do número, que não custa nada. Como o fator N / c é constante, nós o calculamos antecipadamente e tudo parece estar bem, se não fosse por um detalhe - a precisão da representação desse fator.

Considere o caso específico de dividir por 10, que ocorre ao converter números de binário para decimal, e, considerando H = 256, calculamos a constante para multiplicação 256/10 = 25,6 e ocorre um erro de arredondamento no número inteiro 25 (-2,3%) ou 26 (+1,6 %) Então, por exemplo, 100 * 26 = 2600 = 256 * 10 + 40 e a parte mais alta do resultado é 10, o que corresponde à 100/10 esperada = 10. Podemos calcular em quais valores do dividendo o resultado desvia do correto em mais de um, mas para isso teremos que resolver equações da forma

[n/10]=[nk/N]+1

(onde os colchetes significam a parte inteira com arredondamento para baixo), e esse não é um procedimento muito claro, é mais fácil fazer uma simulação numérica e garantir que o resultado esteja correto para um certo n. Você pode inserir um aditivo de correção e compensar a perda de precisão pela fórmula

(26/2+1)/256

e expandir significativamente o intervalo permitido de valores de dividendos (até 256, ou seja, em um número inteiro não assinado de 8 bits, nunca cometemos erros), mas não podemos eliminar a desvantagem fundamental do método - a presença de um erro, além disso, obtemos apenas um cálculo privado do restante (se necessário, mas esse não é o nosso caso), é possível realizar apenas uma operação separada, o que afeta adversamente a velocidade do trabalho. Em geral, o método está funcionando, mas de escopo limitado.

Portanto, fiquei um pouco surpreso quando, no código compilado (como sempre, graças a godbolt pela oportunidade), vi no endereço multiplicação aritmética por uma certa constante (suficientemente grande) em vez de dividir por uma constante (muito pequena). Outra questão era que a constante não era nada semelhante à calculada para o método acima. E, finalmente, o resultado não é a metade mais antiga do trabalho, mas a parte mais jovem. Em geral, um método um pouco estranho, algum lixo, mas os cálculos mostram que funciona. Uma breve reflexão revela o segredo e o método acaba sendo absolutamente correto, mas ... (é claro, o leitor esperava "mas ...", pois é impossível substituir a divisão pela multiplicação no caso geral) ele tem limitações.

Considere o número mágico 143 e examine suas propriedades divertidas 77 * 143 = 11011, sua parte mais jovem 011 = 1 = 77/7. É fácil ver que 784 * 143 = 112112, sua parte mais jovem é 112 = 784/7 e assim por diante para todos os números que não excedam 999 * 7 = 6993. Aqui está, uma limitação natural do método, mas considerando outro número mágico 7143, estenderemos o intervalo de possibilidades para 9999 * 7 = 69993. Não é difícil encontrar outros números mágicos que tenham propriedades mágicas semelhantes.

Como obter esses números - queremos encontrar um número, multiplicando pelo qual o dividendo possa obter um resultado que contenha o resultado da divisão em sua parte mais jovem, neste caso por 7. Parece obscuro, mas é realmente simples, para o número de entrada 7 que queremos obtenha xxx001, suponha xxx = 001 e aqui está a simples magia de rua 1001/7 = 143. Obviamente, 143 * 7 = 1001, então para qualquer número = n * 7, (n * 7) * 143 = n * (7 * 143) = n * 1001 é verdadeiro, e a parte inferior do resultado é n, e assim por diante.

Agora vemos uma desvantagem fundamental desse método - ele funciona apenas para números que são múltiplos do divisor e fornece um resultado de multiplicação completamente imprevisível (no sentido de que não corresponde à divisão) em todos os outros casos. Mas para esta aplicação específica, quando subtrairmos os endereços dos elementos da matriz, o resultado será exatamente um múltiplo do tamanho do elemento da matriz e temos todo o direito de usar esse método. Se tomarmos os números errados, o resultado da divisão também não deve nos interessar: "uma máquina é um moinho; se você atirar pedras nela, a farinha não funcionará".

Encontrando números mágicos para divisores diferentes de 7, bem como a prova de sua existência, deixamos ao leitor curioso. Também é interessante construir um gráfico de fatores dependendo do divisor e ver quedas e picos nele; provavelmente sua presença está de alguma forma correlacionada com a teoria dos números. Por exemplo, para o composto 21 = 3 * 7, esse número = 381 (é claro, o número mínimo, o restante não é do nosso interesse) é claramente menor que o produto 667 * 143 = 95381 e ainda não é múltiplo, como eu pensava ingenuamente, embora 381 = 381.

Outro fato interessante é que os números mágicos serão diferentes em diferentes sistemas numéricos. Por exemplo, no sistema decimal, não há constante para dividir por 5, pois nenhum dos números da forma x ... 1 pode ter um cinco como seu divisor e nosso método não funciona. Mas, ao mesmo tempo, no sistema binário (hexadecimal), esse problema é resolvido, pois 1024 + 1 = 1025 = 0x401 é maravilhosamente dividido com o resultado 205 = 0xCD e nosso algoritmo funciona novamente, por exemplo 130 * 205 = 0x82 * 0xCD = 0xFAFA => FA = 26 = 130/5. Além disso, pode-se provar (bem, acho que sim) que agora o problema de encontrar um fator está resolvido para qualquer divisor ímpar, e qualquer um par se transforma em um ímpar com um número finito de turnos (posso definitivamente provar isso). Na medida em que uma representação binária é conveniente e útil, tivemos muita sorte com ela.

PS.Meus leitores regulares (me elogio com a esperança de que existem) estão perplexos - o post chegou ao fim e onde está o "grito de Yaroslavna". Não se preocupe, meus queridos, lá está ele.

Nas placas de depuração para CC1350 e CC1352, um cristal do comutador de antena é usado (usado de maneira diferente, mas não importa), ou seja, XMSSJE3G0PA (não tente ler isso na transcrição em russo, tenho certeza de que o muRata não tinha nada disso em mente). Portanto, um switch com características modestas - uma faixa de frequência de até 2,7 GHz, atenuação de passagem - 0,28 dB, atenuação de isolamento - 33 dB, potência de comutação - 20 dB. Mas tudo isso é compensado por dois parâmetros - dimensões 1,5 * 1,5 mm e custo - US $ 0,9, apesar das tecnologias aplicadas "Silicon On Isolator" e "arseneto de gálio".

COMO eles fazem isso - estou falando principalmente sobre o preço e, em segundo lugar - por que não fazemos isso - a pergunta é retórica

Source: https://habr.com/ru/post/pt430732/


All Articles