<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexey Bolshakov</title>
    <description>The latest articles on DEV Community by Alexey Bolshakov (@ua3mqj).</description>
    <link>https://dev.to/ua3mqj</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg</url>
      <title>DEV Community: Alexey Bolshakov</title>
      <link>https://dev.to/ua3mqj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ua3mqj"/>
    <language>en</language>
    <item>
      <title>Forth made neural networks suffer</title>
      <dc:creator>Alexey Bolshakov</dc:creator>
      <pubDate>Tue, 09 Jun 2026 12:06:58 +0000</pubDate>
      <link>https://dev.to/ua3mqj/forth-made-neural-networks-suffer-4p7h</link>
      <guid>https://dev.to/ua3mqj/forth-made-neural-networks-suffer-4p7h</guid>
      <description>&lt;p&gt;... and My Cursor Bill&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Read the original in Russian: &lt;a href="https://dev.to/ua3mqj/forth-zastavil-stradat-nieirosieti-2aih"&gt;Forth Made Neural Networks Suffer (and My Cursor Bill)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Fail of the Week
&lt;/h1&gt;

&lt;p&gt;Inspired by the fact that with AI I managed to write a &lt;a href="https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p"&gt;package manager for Forth&lt;/a&gt; literally in one evening, plus several modern utilities, I started thinking. It works out well, of course. But it's somehow expensive. What if I try to train a neural network and run it locally? I'd save money and gain experience!&lt;/p&gt;




&lt;p&gt;For most modern programming languages, neural networks are quite ready. They were trained on metric tons of examples. But Forth somehow got skipped. I searched coder models for a list of languages they were trained on and couldn't find a model that lists Forth as supported. If we're talking about using models in Cursor, the most accessible agent—Auto—can't handle Forth programming at all. Here I "lost it" again and said—"let's go all in" (Opus 4.7)—solve it! In that mode the models actually started getting somewhere. Sometimes not on the first try. Sometimes really not on the first try, but still successfully.&lt;/p&gt;

&lt;p&gt;Overall, Forth turned out to be a &lt;strong&gt;tough nut&lt;/strong&gt; for neural networks: top models output something, but slowly, painfully, rewriting dozens of times, rearranging words, running tests, and accumulating context. Forth made &lt;strong&gt;everything grind&lt;/strong&gt;—the hardware, and my wallet too. Not a triumph-of-Forth story—a slow lesson in how neural networks and the tooling around them actually work, while chasing an atypical AI problem and, as always, stepped on every rake. The goal shifted: build &lt;strong&gt;AI that can beat Forth&lt;/strong&gt; without going broke.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is frules
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3y2awpjos9l5z7jjdip7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3y2awpjos9l5z7jjdip7.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simple answer—you explain to AI how to write Forth and... Profit? Not so fast!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/VitaSound/frules" rel="noopener noreferrer"&gt;frules&lt;/a&gt;&lt;/strong&gt; is not "yet another ChatGPT prompt"&lt;/p&gt;

&lt;p&gt;I had a hypothesis that AI simply doesn't know what Forth is and you just need to give it all the information. I thought that, of course, not out of nowhere, but after long chat dialogues with AI. After which we concluded that we need to assemble a knowledge dataset, and then the model would be able to program!&lt;/p&gt;

&lt;p&gt;From what I knew, I asked AI to download the entire Gforth manual and examples of working with it. Write theses on Forth programming. I also added Brodie's book to the dataset, converting it to MD along the way (yay! now you can &lt;a href="https://github.com/VitaSound/frules/blob/main/sources/brodie-thinking-forth/chapter1.md" rel="noopener noreferrer"&gt;read it online&lt;/a&gt;). After that, many generalized theses AI had already formed were rewritten almost completely. Plus, we tried to make a block of typical programming tasks, like on Codewars. The hardest part was that there were no solutions to the tasks, and I spent a lot of time and money having AI solve all 98 tasks so the solutions would pass the tests. In places it was genuinely painful. Because toward the end there were about 10 tasks at level 7 out of 10 on the difficulty scale. And each one took at least an hour.&lt;/p&gt;

&lt;p&gt;From previous dialogues with AI it became clear that this stage would have to be solved on the smartest models. Set the right tasks, solve them correctly, and get working examples. A weak model won't solve the task, and won't even write a correct test. By solving tasks and getting the right answer, we essentially "extract" from the large model how it was trained to solve problems. On one task that no AI could solve in 2–3 attempts, I just checked the tests myself. And it turned out the test itself was wrong—incorrect expected answers were specified (neural network hallucinations), and one model fooled another, while the other vainly tried to solve the correct problem and verify it with the wrong test. As always, the third party suffered (me).&lt;/p&gt;

&lt;p&gt;Then, after passing that checkpoint, I added library code from theForthNet and tasks from Rosetta with solutions (dang, I didn't have to solve those—everything's there!). But when the models analyzed it, things weren't that simple. First, the original dataset was logically split across knowledge areas so each had several solutions. And Rosetta tasks turned out to be mostly duplicates. So we took only about 10 of them, not 500+. Other tasks there were game-like and wouldn't (in AI's opinion) help learn programming. But the dataset could still be expanded. It also turned out the solutions exist, but they don't pass tests on gforth. From that point AI decided to start fixing those tests, but I stopped the process, realizing I couldn't endure another sprint of fixing broken tasks emotionally or financially—and we hadn't even gotten to training yet.&lt;/p&gt;

&lt;p&gt;So the hardest part is behind me, I thought. The dataset is ready. But AI doesn't need all the material in raw form! It needs to take only specific distillations. At this stage AI, having processed everything, did what it calls "distillation" and formed a set of &lt;code&gt;.mdc&lt;/code&gt; files on how to program. Grouping that information by topic along the way.&lt;/p&gt;

&lt;p&gt;What came out in the end&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;rules&lt;/strong&gt; (&lt;code&gt;.mdc&lt;/code&gt;) — distillation of Gforth manual, Brodie, Rosetta, theForthNet;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;eval&lt;/strong&gt; — &lt;strong&gt;151&lt;/strong&gt; challenges (&lt;strong&gt;98&lt;/strong&gt; train / &lt;strong&gt;53&lt;/strong&gt; hold-out blind);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gforth&lt;/strong&gt; — judge: every gold solution marked &lt;code&gt;TESTS OK&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We got a library—a knowledge base you can install (via script) into your Forth project, and when working with AI it will use all these rule files.&lt;/p&gt;

&lt;p&gt;Cursor picks up rules from &lt;code&gt;.cursor/rules/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But in principle, I think you can read it yourself and understand something. It's hard to evaluate all of this. Validating everything alone takes a long time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Training your own model — Track A
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flefpz2iqj4cfdx1n1xgf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flefpz2iqj4cfdx1n1xgf.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Background. Before starting to "really" train a model—with data, on GPU—I had discovered RAG at work as a data source for the model. That is, we take a general-purpose model and connect an external vector database. We got a model of how our production is organized and could ask questions and get answers. Building a vector DB turned out to be an art form of its own. I watched from the sidelines as AI wrote scripts to "correctly" lay data into vectors. Then I'd ask questions and AI answered not how I wanted. And then the programming AI fixed the indexes. At that point I had a weak understanding of how it all works "under the hood." But I came to understand that for some of my work questions I don't need AI at all. I need to build tooling that fetches data, processes it, and outputs results. That knowledge would come in handy a bit later...&lt;/p&gt;

&lt;p&gt;Assuming RAG wasn't quite what I wanted—I wanted to master the fine-tuning pipeline with my own data. That case where datasets are prepared somehow. Then you train the neural network and an extra layer of knowledge grows on it and boom—the model learned to program in Forth! Cool (I thought). My understanding at the time was: Model+RAG is when we have constantly changing knowledge. We easily swap the vector DB and don't need to retrain the model. But if we need to "bake" knowledge into the model—that's when we train it.&lt;/p&gt;

&lt;p&gt;So I wouldn't wait forever for results, I asked AI to create this Track A for me: take the smallest 0.5B model, fine-tune it, see what it learned, understand the dataset assembly pipeline, training launch, and output. And only after that move on to larger models we'd train longer.&lt;/p&gt;

&lt;p&gt;Well. My laptop has a 4070 with 8 GB and training is actually quite fast: literally a few minutes. The model is small. What did I get? A network that outputs repeatedly repeating nonsense instead of Forth code. Then we trained longer—added repetitions, I started staring at the numbers and felt I was starting to understand: epochs, error percentage. That percentage started dropping. But the final model still output a mix of Python code and Forth words.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t8kk9vufomw6hrcd9l0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t8kk9vufomw6hrcd9l0.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we discussed the result with AI and how to improve it. I wanted the network to output something more useful. Give it more data. But then I was reminded that I wasn't really supposed to get a normal result, because the goal was—to master the tooling pipeline! Damn. Still, I did manage to see the model learned something. At that moment Track A should have been closed—chalk it up as the first pancake—and move on (without trying to get something meaningful from the small model). But... My AI said there was a bug in my scripts: data was truncated and the model trained on the wrong stuff. OK—fix it! We trained on proper data. And miracle: the model... didn't learn to program in Forth, but there was progress! It started formatting results as syntactically valid Forth definitions &lt;code&gt;: forth-word commands ;&lt;/code&gt; and I was even a little happy.&lt;/p&gt;

&lt;p&gt;The Track A stage was decided closed—no more fine-tuning the 0.5B model. Why? Because the "sandbox" goal is done, but the dream "0.5B model learns Forth"—isn't. What remained was understanding why—it's not too few epochs, repetitions, or data. It's something fundamental I hadn't grasped yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to take stock
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Pause after Track A—questions before buying more GPU.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Many questions piled up, and without answers we couldn't move forward. Wild impulses started: buy my own powerful computer and GPU (which one?), maybe go to the institute, there's a cluster of 4×A100, or rent cloud. But for what?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdi14swh63fdryg5oapd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdi14swh63fdryg5oapd.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While solving tasks, AI throws a lot of ML terminology. I collected them and started discussions. Plus, after experiments and measurements—you need to interpret results correctly, draw conclusions, and I didn't know how. So we had a long phase of talking to understand what we'd done, what conclusion to draw, and what to do next. We talked and talked, new questions appeared, new terms appeared. I asked follow-up questions on them. It's a good mechanism to learn what you don't know (we can't know what we don't know). We gradually spun up the dialogue.&lt;/p&gt;

&lt;p&gt;Some questions I asked repeatedly, coming back many times. Apparently I just didn't want to believe some of my misconceptions.&lt;/p&gt;

&lt;p&gt;The small model was planned from the start only for debugging the training pipeline and learning the tooling. But seeing the first results, I forgot all that and wanted to improve the network to get the right result—Forth code. And I stubbornly looked for ways to solve that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stages of accepting the futility of building your own Forth-programming network
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dfsm9em2njtkk6kxn1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dfsm9em2njtkk6kxn1g.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI: &lt;em&gt;The 0.5B model is too small and can't be trained. With that capacity it practically can't do anything. And it won't output Forth results and can't. Even top cloud models struggle.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before I accepted that thought, a whole chain of learnings and realizations happened. Surely not all of them are correct.&lt;/p&gt;

&lt;p&gt;Realization +1: RAG is just text in the prompt. There is no direct connection between the neural network and the vector DB. There's no secret socket into the vector DB. All RAG data is inserted as text together with my original question in the prompt. All of that lands in context. The model understands that and starts answering accordingly.&lt;/p&gt;

&lt;p&gt;Knowledge +1: Fine-tuning with LoRA—patterns baked into the weights via a thin adapter on a frozen base. You can merge and get a new model. But on my PC that's unrealistically slow. I may be wrong, but merge hits CPU and disk, and GPU doesn't help at all. So these are two different ways to add knowledge to the network. But with RAG—context is spent; with LoRA—it isn't.&lt;/p&gt;

&lt;p&gt;Realization +1: LoRA doesn't replace RAG/rules in context. And context is only what exists—facts, theses. But overall, an answer with RAG can be better than from training.&lt;/p&gt;

&lt;p&gt;Debating with AI about how and why to train a model—partial understanding appeared of why you shouldn't train too long on the same data, minimizing error. Train too long and the answer gets "memorized," but you want an answer through reasoning. That's why not every task in the dataset has an answer. So we teach the network via task–answer, then give a new task and expect an answer through reasoning. But we want an independent solution from the network. Then, having its reasoning—the answer—we can validate it because we have the correct answer for the task. Alas. It seems 0.5B isn't enough even to memorize answers.&lt;/p&gt;

&lt;p&gt;Knowledge +1: Hold-out ("set aside")—tasks that weren't in training (neither in challenge-train.jsonl nor in train-merged for that slug). Used only to test the model blind, not how well it memorized the textbook.&lt;/p&gt;

&lt;p&gt;What can you expect from a 0.5B network at all? And what to expect from a network in general? How does it work? We use networks and it's some kind of magic. Understanding how they work—that's higher magic. But at least understand what and how it answers.&lt;/p&gt;

&lt;p&gt;Knowledge +1: a network is basically a predictor of the next correct token given the current context.&lt;/p&gt;

&lt;p&gt;What? What do you mean?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64m9p3ry1zpgyott9vr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64m9p3ry1zpgyott9vr8.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Realization +1: understanding how AI "thinks," what it can output, which tasks it can solve and which it can't—one of the key moments for getting a developer tool.&lt;/p&gt;

&lt;p&gt;And here I start guessing I'm going the wrong way entirely. I want direct Forth code generation from the neural network! But expecting reverse Polish notation (RPN (1 2 +)) generation directly from the network is a mistake. Neither because of small training data volume nor because of its nature—generating words sequentially—it can't undo its words and rearrange them (without an external tool).&lt;/p&gt;

&lt;p&gt;Realization +1: services like Cursor just build a set of tools around a "thinking" model. Textual ones, because there's no other way to communicate. The model has one primitive: &lt;strong&gt;question → answer&lt;/strong&gt;. What looks like an Agent with "reasoning" in the UI—&lt;strong&gt;Cursor&lt;/strong&gt; under the hood: just several skills and MCP tools.&lt;/p&gt;

&lt;p&gt;But the thought still won't leave me alone. Why do 0.5B models exist then?&lt;/p&gt;

&lt;p&gt;Remember: no network runs Forth in its head, doesn't imagine a stack, doesn't keep one in memory. It can use context as a notepad, like a human. But you need a much larger network. Small models can do very simple things, like autocomplete.&lt;/p&gt;

&lt;p&gt;Along the way it turned out model size is fixed too. LoRA won't "grow" 0.5B to other sizes by adding data. The network always stays the same size.&lt;/p&gt;

&lt;p&gt;Then I tried to understand what we were actually training the network on. For example, I trained it on word definitions: factorial, fibonacci, etc. I saw loss change. It drops; seems like train longer and all will be well. I asked what that affects. But in the end I was told that loss down to 1 is normal training; below that is memorization. And there's no correct answer to when to stop training. Train to very low error and on prompt fib you get an exact, memorized answer. Like a student with 10 years of Russian but given some English words to memorize. They'll memorize them and only say those. But can't do anything else.&lt;/p&gt;

&lt;p&gt;I also tried to understand how a neural network as a tree differs from a tree of Forth words and concepts. The model can understand and classify any part of a Forth expression. Each word is a separate concept. Maybe train on simple stack operations first, addition, etc. Turns out I didn't train the model on basics and immediately asked it to solve hard Forth tasks. Maybe that's why it doesn't solve them?&lt;/p&gt;

&lt;p&gt;Knowledge +1: you can't make a model that writes Forth from a Python-trained model on 100 examples.&lt;/p&gt;

&lt;p&gt;Knowledge +1: expecting exact formulations from a network is a mistake. The nature of the network is to err in small details and syntax—and it will definitely err in Forth syntax. Models are trained long and hard to generate JSON. Training a model from scratch—a home user doesn't have those resources (technical or dataset-wise).&lt;/p&gt;

&lt;p&gt;But what did we achieve? At first I got a very strange answer from the small model. Then I fine-tuned. Saw the loss drops mentioned above, but nothing got better. Then it turned out there was prompt desync—an error in training data scripts. After fixing, answers looked somewhat like the truth. But only somewhat:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Word&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Reference (tests/ans/)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gcd&lt;/td&gt;
&lt;td&gt;begin dup 0= … while dup 0= … dup 1 swap gcd&lt;/td&gt;
&lt;td&gt;begin dup while tuck mod repeat drop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;factorial&lt;/td&gt;
&lt;td&gt;0= if drop exit then 1* factorial&lt;/td&gt;
&lt;td&gt;dup 1&amp;lt;= if drop 1 exit then dup 1- recurse *&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;divisible?&lt;/td&gt;
&lt;td&gt;1 div 0= if 1 else 0&lt;/td&gt;
&lt;td&gt;mod 0=&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The word definition was wrapped in the correct format &lt;code&gt;: word ( … ) … ;&lt;/code&gt;—already not markdown/return format as before. But logic is wrong on all three. gforth fairly fails the tests.&lt;/p&gt;

&lt;p&gt;The model doesn't copy strings from jsonl (gcd is in train)—0.5B can't hold the algorithm even with correct SFT.&lt;/p&gt;

&lt;p&gt;But the smoke test wrote "OK" because it only checked for ":" at the start of the definition and ";" at the end. It doesn't compare to reference or require TESTS OK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5beugub9vv7fnq6s5xub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5beugub9vv7fnq6s5xub.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Train had no basics in order of increasing difficulty (curriculum)—went straight to hard tasks (gcd/factorial). The table above is the outcome: even honest SFT didn't save it. Track A closed for good.&lt;/p&gt;

&lt;p&gt;But the idea kept spinning in my head that logically a Forth tree and a neural network thinking tree are very similar and generating Forth code for a neural network is like writing down the classification path of what you realized. But I can't explain how to express that. And I don't know if that understanding can be applied to the result.&lt;/p&gt;

&lt;p&gt;Again and again we discussed this and each time AI explained I can't do it that way. Better to get from AI a properly described algorithm in a language AI knows, or pseudocode, and use an external conversion utility to get Forth code! Because the utility does it fast, cheap, and accurately. Unlike the neural network, which does it by processing huge data arrays and still has a nonzero chance of erring on some letter. It's like hammering nails with a microscope.&lt;/p&gt;

&lt;p&gt;After that I gave up. And admitted we have to do it that way. Plus I was already guessing that working directly with syntax and notation isn't programming yet. You still need to understand algorithms, data structures, and have experience solving tasks with algorithms and structures. Algorithmic tasks! And Coder models are trained for exactly that—you should use their output. And convert that output to Forth non-neurally.&lt;/p&gt;

&lt;p&gt;How? I started asking—can models output AST? That's almost Forth. No direct answer that some model outputs AST was found, but per AI, during training models saw millions of trees from compiler textbooks. But to get AST, programmers supposedly ask for the answer as Lisp S-expressions.&lt;/p&gt;

&lt;p&gt;Task: 1 + (2 * 3)&lt;/p&gt;

&lt;p&gt;AST (Lisp format, which models generate well): (+ 1 (* 2 3))&lt;/p&gt;

&lt;p&gt;Transpiler output to Forth: 1 2 3 * +&lt;/p&gt;

&lt;p&gt;Forth and Lisp are mirrors. You can express one in terms of the other.&lt;/p&gt;

&lt;p&gt;What else? Assembler? Not quite. Assembler for a stack VM? Yes!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stack languages LLMs know 10 out of 10:
WebAssembly (WASM): In text form (.wat) WASM is a pure stack machine.
Java bytecode (JVM): JVM computation is strictly on the operand stack.
PostScript (PS): PDF printer language. Plot twist: PostScript is a Forth dialect! Same RPN, same words dup, pop (drop analog), exch (swap analog).
CIL (.NET bytecode) and Python bytecode (dis) — also stack-based.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I conclude the final Forth result can come another way—through an external conversion tool. As part of the toolset around the AI model—the so-called "factory."&lt;/p&gt;

&lt;p&gt;I decided to stop at WASM&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Look how strikingly similar WASM is to Forth:
WASM code (model writes it without errors):

get_local $a
get_local $b
i32.add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we dumbly replace and get Forth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkze7yz705vxea4ou68kq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkze7yz705vxea4ou68kq.png" alt=" " width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, let's sum up again:&lt;/p&gt;

&lt;p&gt;My thesis: "Forth should be easy for a tree/neural network"&lt;/p&gt;

&lt;p&gt;AI's answer: Forth is easy for a compiler, not for an autoregressive LLM:&lt;/p&gt;

&lt;p&gt;LLM generates left to right; RPN requires "holding the operation in your head" while placing operands. Forth mixes data and metaprogramming (immediate, ['], create/does&amp;gt;)—the transformer doesn't "execute" that, only imitates text. So your chain "parse task → write as tokens" is right—but the middle (parse → RPN) must be an algorithm, not the network, and certainly not a 0.5B network.&lt;/p&gt;

&lt;p&gt;After that I wanted to clearly separate "tool vs task" and which of my tasks should be solved by neural networks and which by "factory" ecosystem tools.&lt;/p&gt;

&lt;p&gt;What not to expect from AI (architectural limits):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute code, hold a stack "in its head," rollback on error—no internal VM; only text.&lt;/li&gt;
&lt;li&gt;Guaranteed correctness—never; only probability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we talked a bit about training a model from scratch (not retuning from Python to Forth, but training on Forth from zero). That process is unviable for home development. You'd need to teach all levels of programming. But even with lots of money and specialists—the idea of generating Forth text is wrong. The network will still err; if not, that's hammering nails with a microscope.&lt;/p&gt;

&lt;p&gt;But what can we do? We can build a "Factory" from the toolchain!&lt;/p&gt;

&lt;p&gt;So we can get from a large model the essence of the algorithm and code in notation convenient for Forth translation. Convert with tools. Verify with tools. Deliver the answer. Thus we spare the large model the pain, suffering, and huge token spend of programming in Forth.&lt;/p&gt;

&lt;p&gt;So the task changed a lot philosophically: AI shouldn't write Forth itself—it should use external tools to get Forth and verify the result. And for validating conversion, the Forth ecosystem tools built in a few days help: fmix + test, lint flint, fcov coverage.&lt;/p&gt;

&lt;p&gt;If it works, we get Forth code cheaper. If Cursor+Opus runs on this tools functionality, results should be cheaper.&lt;/p&gt;

&lt;p&gt;Thus we arrived at understanding and reinvented (the wheel)—what everyone calls skills/tools.&lt;/p&gt;

&lt;p&gt;Realization: there are no monolithic AI neural nets that program. We don't use them. We use AI ecosystems that eventually produce program code. At home we run one mono model. Then we're surprised—why is it so dumb. But we're comparing one small model to a big set of large models and tools around them.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI terminology
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5oie7t8nt0towpiojr0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5oie7t8nt0towpiojr0y.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Terms AI actively used explaining how it works. There's a funny bit: we discuss AI with AI )) I'll give them as AI explained them to me. In the context of our dialogue. I think it's better to look up each word online for a more correct explanation. Here the list itself is what matters.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;In plain words&lt;/th&gt;
&lt;th&gt;What happens technically&lt;/th&gt;
&lt;th&gt;For Forth / frules&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parameter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Network "memory cell"&lt;/td&gt;
&lt;td&gt;One trainable number in a matrix&lt;/td&gt;
&lt;td&gt;0.5B = 500 million such numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0.5B / 7B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model size / capacity&lt;/td&gt;
&lt;td&gt;Parameter count; more → more patterns&lt;/td&gt;
&lt;td&gt;0.5B — too little for stack algorithms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pretrain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"School of life" on the internet&lt;/td&gt;
&lt;td&gt;Predict next token on huge corpus&lt;/td&gt;
&lt;td&gt;Forth ≈ 0%; Python, C, "code in general" plenty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Token&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chunk of text (~word/part)&lt;/td&gt;
&lt;td&gt;LLM input/output unit&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;: gcd&lt;/code&gt; may be 2–3 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transformer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LLM architecture&lt;/td&gt;
&lt;td&gt;Attention: each token "looks at" previous ones&lt;/td&gt;
&lt;td&gt;No stack, no VM — only text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SFT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supervised fine-tuning&lt;/td&gt;
&lt;td&gt;Textbook: prompt → reference answer&lt;/td&gt;
&lt;td&gt;Your &lt;code&gt;train-merged.jsonl&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Instruct / instruction-tuned&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Assistant" model&lt;/td&gt;
&lt;td&gt;SFT on "question → answer" dialogues&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Qwen2.5-Coder-0.5B-**Instruct**&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Thin "overlay"&lt;/td&gt;
&lt;td&gt;Few new weights; base frozen (QLoRA: 4-bit)&lt;/td&gt;
&lt;td&gt;~17 MB on top of 0.5B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QLoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LoRA + compressed base&lt;/td&gt;
&lt;td&gt;Base in 4-bit, only adapter trains&lt;/td&gt;
&lt;td&gt;Track A on RTX 4070&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loss&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Guessing error&lt;/td&gt;
&lt;td&gt;Cross-entropy: how far off on next token&lt;/td&gt;
&lt;td&gt;Low loss ≠ working Forth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Epoch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One pass over entire jsonl&lt;/td&gt;
&lt;td&gt;How many times model saw each line&lt;/td&gt;
&lt;td&gt;3 ep on 139 lines — little for generalization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overfit / memorization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Memorized train, doesn't transfer&lt;/td&gt;
&lt;td&gt;Great on same prompts, bad on new&lt;/td&gt;
&lt;td&gt;Old run: memorized &lt;strong&gt;rules&lt;/strong&gt;, not gcd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Generalization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transfer to new&lt;/td&gt;
&lt;td&gt;Works on prompts/tasks outside train&lt;/td&gt;
&lt;td&gt;0.5B + Forth: practically none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memorization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Copying train pairs&lt;/td&gt;
&lt;td&gt;Answer ≈ string from jsonl&lt;/td&gt;
&lt;td&gt;0.5B didn't even do that for gcd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Curriculum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple to complex&lt;/td&gt;
&lt;td&gt;First &lt;code&gt;1 2 +&lt;/code&gt;, then &lt;code&gt;: square&lt;/code&gt;, then gcd&lt;/td&gt;
&lt;td&gt;Not in frules; needed for small models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hold-out&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tasks not in train&lt;/td&gt;
&lt;td&gt;Verification only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;eval_holdout&lt;/code&gt; in &lt;code&gt;tests/challenges/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;System prompt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Role / rules in chat&lt;/td&gt;
&lt;td&gt;First message &lt;code&gt;role: system&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;SFT: short (~50 tok); Ollama: full rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reasoning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Step chain to answer&lt;/td&gt;
&lt;td&gt;Model generates intermediate steps (CoT)&lt;/td&gt;
&lt;td&gt;Forth stack = needs "scratchpad" or algorithm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CoT (chain-of-thought)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Think aloud" in text&lt;/td&gt;
&lt;td&gt;Answer: stack, analysis, then code&lt;/td&gt;
&lt;td&gt;Helps &lt;strong&gt;large&lt;/strong&gt; models; 0.5B breaks on long chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rules (frules)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Text in prompt, not weights&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.mdc&lt;/code&gt; in SYSTEM; weights unchanged&lt;/td&gt;
&lt;td&gt;Cursor / Ollama — stronger than LoRA 0.5B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merge LoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Merge adapter into base&lt;/td&gt;
&lt;td&gt;Full weights for Ollama/GGUF&lt;/td&gt;
&lt;td&gt;Separate from Forth quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GGUF / Ollama&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local inference format&lt;/td&gt;
&lt;td&gt;Quantized model for chat&lt;/td&gt;
&lt;td&gt;Rules in Modelfile ≠ train jsonl&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Side products of this exciting week
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1hvrps1zwwlc307k3pz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1hvrps1zwwlc307k3pz.png" alt=" " width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While I was doing all this, some tools were created along the way. I wanted to estimate what we got: by time period, functionality, and code volume.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repo&lt;/th&gt;
&lt;th&gt;Period&lt;/th&gt;
&lt;th&gt;Versions&lt;/th&gt;
&lt;th&gt;In one line&lt;/th&gt;
&lt;th&gt;Code*&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fmix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2024 → 24.05&lt;/td&gt;
&lt;td&gt;0.7.x&lt;/td&gt;
&lt;td&gt;Package manager&lt;/td&gt;
&lt;td&gt;~1.2k LOC &lt;code&gt;.4th&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fsemver&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24.05, 1 day&lt;/td&gt;
&lt;td&gt;0.1.x&lt;/td&gt;
&lt;td&gt;Semver library (for fmix/flint)&lt;/td&gt;
&lt;td&gt;~360 LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fcov&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24.05, 1 day&lt;/td&gt;
&lt;td&gt;0.1→&lt;strong&gt;0.3&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Coverage tool: console/JSON/LCOV/HTML&lt;/td&gt;
&lt;td&gt;~2.8k LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;flint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24.05, 1 day&lt;/td&gt;
&lt;td&gt;0.1→&lt;strong&gt;0.2.2&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Lint duplicate &lt;code&gt;: word&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;~825 LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fenum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;22.05&lt;/td&gt;
&lt;td&gt;0.1.x&lt;/td&gt;
&lt;td&gt;Library for &lt;code&gt;ulist&lt;/code&gt; and &lt;code&gt;enum&lt;/code&gt; (used in flint/fcov)&lt;/td&gt;
&lt;td&gt;~750 LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fhdlgen&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20–24.05&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;DSL→Verilog converter (next time)&lt;/td&gt;
&lt;td&gt;~2k LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;frules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25–31.05&lt;/td&gt;
&lt;td&gt;0.1.x&lt;/td&gt;
&lt;td&gt;151/98/53, Track A closed, docs hub&lt;/td&gt;
&lt;td&gt;gold ~6.5k; rules ~2.1k md&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;*LOC excluding &lt;code&gt;forth-packages/&lt;/code&gt; — just to gauge scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;May 24&lt;/strong&gt; — flint, fcov, fsemver written in one day with Opus 4.8. &lt;strong&gt;frules&lt;/strong&gt; — six more days: challenge bank, gold, first checks (Track A), Forth docs hub.&lt;/p&gt;

&lt;p&gt;Usually I hate wasting money on nothing. Not here: I don't regret the money or sleepless nights. Learned and realized too much in those days. Many skills helped on my day job too.&lt;/p&gt;

&lt;h2&gt;
  
  
  In short: antipatterns
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;"Opus will write all the &lt;code&gt;: word ... ;&lt;/code&gt; code" — overhard + overkill + overinvoice.&lt;/li&gt;
&lt;li&gt;"LoRA will learn postfix" — no (Track A closed).&lt;/li&gt;
&lt;li&gt;Expecting a "factory" from 0.5B — you get an intern, not an assembly line.&lt;/li&gt;
&lt;li&gt;Generating Forth directly when you need IR → transpiler.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcway3gsbrejm1jthhe8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcway3gsbrejm1jthhe8.png" alt=" " width="798" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Five paid invoices in one week (May 23–29): $75.06 + $50.85 + $34.66 + $75.00 + $27.84 ≈ $263. The bill climbs with &lt;code&gt;rot&lt;/code&gt; and thinking.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72olrggs0hetflxz4zz2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72olrggs0hetflxz4zz2.gif" alt=" " width="560" height="996"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What did we learn?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp96sx7wc2ulnv5hzulm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp96sx7wc2ulnv5hzulm2.png" alt=" " width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;- What did we learn, Palmer?&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- I don't know, sir.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- I don't know either. I guess we learned not to do it again.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- Yes, sir.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- I'm not sure what we actually did.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- Yes, sir. It's… hard to say.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- Good grief.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;VitaSound:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/frules" rel="noopener noreferrer"&gt;frules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/feco" rel="noopener noreferrer"&gt;feco&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/fmix" rel="noopener noreferrer"&gt;fmix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/fmcp" rel="noopener noreferrer"&gt;fmcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/flint" rel="noopener noreferrer"&gt;flint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/fcov" rel="noopener noreferrer"&gt;fcov&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/VitaSound/fsemver" rel="noopener noreferrer"&gt;fsemver&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be continued ...&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>forth</category>
      <category>toolchain</category>
    </item>
    <item>
      <title>Forth заставил страдать нейросети</title>
      <dc:creator>Alexey Bolshakov</dc:creator>
      <pubDate>Tue, 09 Jun 2026 07:53:40 +0000</pubDate>
      <link>https://dev.to/ua3mqj/forth-zastavil-stradat-nieirosieti-2aih</link>
      <guid>https://dev.to/ua3mqj/forth-zastavil-stradat-nieirosieti-2aih</guid>
      <description>&lt;p&gt;... и мой Cursor-счёт&lt;/p&gt;

&lt;h1&gt;
  
  
  Fail недели
&lt;/h1&gt;

&lt;p&gt;Вдохновившись тем, что с помощью ИИ мне удалось написать буквально за вечер &lt;a href="https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p"&gt;пакетный менеджер для Forth&lt;/a&gt;, а так-же несколько модерновых утилит я задумался. Получается, конечно, хорошо. Но как-то дорого. А что, если попробовать обучить нейросеть и запускать ее локально? И денег сэкономлю и опыт получу!&lt;/p&gt;




&lt;p&gt;Для большинства современных языков программирования, нейросети вполне готовы. Их обучали на килотоннах примеров. Но вот Forth каким-то образом обошли. Я поискал coder сети на предмет списка языков, на которые обучали, и не смог найти сеть, где есть Forth в списке поддерживаемых. Если говорить о вариантах использования моделей в cursor, то самый доступный агент=Auto вообще не справляется с программированием на Forth. Тут я опять "психанул" и говорю - "а давай на все деньги" (Opus 4.7) - решай! В таком режиме у компьютерных мозгов начало даже что-то получаться. Иногда не с первого раза. Иногда прям очень не с первого раза, но все же успешно.&lt;/p&gt;

&lt;p&gt;Вообще, Forth оказался &lt;strong&gt;крепким орешком&lt;/strong&gt; для нейросетей: топовые модели что-то выдают, но долго, мучительно, десятки раз переписывая, переставляя слова, запуская тесты и накапливая контекст. Forth заставил &lt;strong&gt;как следует поскрипеть железные шестерёнки&lt;/strong&gt; — и мой кошелёк тоже. Это не история о том, как «Forth победил AI». Это история о том, как я постепенно начал приходить к пониманию того, как работают нейронные сети и экосистема вокруг них, решая нетипичную для ИИ задачу и как всегда, собрав все возможные грабли. История о том, как я начал приходить к пониманию того, как сделать "AI, который победит Forth". История о том, как это сделать эффективнее, не оставшись без денег.&lt;/p&gt;




&lt;h2&gt;
  
  
  Что такое frules
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3y2awpjos9l5z7jjdip7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3y2awpjos9l5z7jjdip7.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ответ прост - объясняешь ИИ, как писать на Forth и ... Profit? А вот и нет!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/VitaSound/frules" rel="noopener noreferrer"&gt;frules&lt;/a&gt;&lt;/strong&gt; — это не «ещё один prompt для ChatGPT»&lt;/p&gt;

&lt;p&gt;У меня появилась гипотеза, что ИИ просто не знает, что такое Forth и надо ему просто дать всю информацию. Подумал я так, конечно же, не на ровном месте, а после длительных диалогов в чате с ИИ. После чего мы пришли к тому, что нужно собрать датасет со знаниями, и уж тогда модель сможет программировать!&lt;/p&gt;

&lt;p&gt;Из того, что я знаю, я попросил ИИ скачать весь GForth мануал и примеры работы с ним. Написать тезисы по программированию на Forth. Так же добавил в датасет источники книгу Brodie попутно преобразовав ее в формат MD (ура! теперь ее &lt;a href="https://github.com/VitaSound/frules/blob/main/sources/brodie-thinking-forth/chapter1.md" rel="noopener noreferrer"&gt;можно читать онлайн&lt;/a&gt;). После этого многие обобщенные тезисы, что ИИ уже сформировала, переписались почти полностью. Плюс к этому, мы попытались сделать блок из типичных программерских задач, как это происходит на Codewars. Сложнее всего оказалось то, что решений задач нет, и я спустил кучу времени и денег на то, чтобы ИИ мне решила все 98 задач таким образом, чтобы решения проходили тесты. Местами было прям больно. Потому что под конец осталось порядка 10ти задач уровня 7 из 10 по шкале сложности. И на решение каждой уходило минимум по часу времени.&lt;/p&gt;

&lt;p&gt;Из предыдущих диалогов с ИИ стало ясно, что этот этап придется решать на самых умных моделях. Ставим правильные задачи, правильно решаем и получаем рабочие примеры. Слабая сеть и задачу не решит, и даже тест правильный не напишет. Решая задачи и получая правильный ответ, мы, по сути, "вытаскиваем" из большой модели то, как она научена решать задачи. Вообще, на одной задаче, которую за 2-3 захода ни одна ИИ не могла решить, я просто взял и проверил тесты, уже сам. И выяснилось, что в принципе сам тест составлен не правильно - были не верные ответы указаны (галлюцинации нейросети), и вышло так, что одна сеть обманула другую, а другая тщетно пыталась решить правильное задание, и проверить его неправильным тестом. Пострадавшим, как всегда, оказалась третья сторона (я).&lt;/p&gt;

&lt;p&gt;Потом, пройдя эту контрольную точку, я добавил просто код библиотек с theForthNet и задачи из Rosettа с решениями (чооорд, можно было не решать, там все есть!). Но анализ электронными мозгами показал, что не всё так просто. Во-первых, изначальный датасет у нас был логически разбит по разным областям знаний, чтоб по кажому было несколько решений. И задачи из Rosetta оказались в основном дублирующими. Поэтому из них взято всего порядка 10 штук, а не 500+. Другие задачи там были игровые и никак (по мнению ИИ) не помогли бы научиться программировать. Но можно было всё же, расширить датасет. Еще выяснилось, что решения то есть, но на gforth они не проходят тесты. С этого момента ИИ решила начать исправлять эти тесты, но я этот процесс остановил, поняв, что еще раз спринт по починке неработающих задач я уже ни эмоционально ни экономически не перенесу, а мы ведь еще и до обучения не дошли.&lt;/p&gt;

&lt;p&gt;Итак, самое сложное позади, подумал я. Датасет готов. Но в чистом виде весь материал не нужен ИИ! Ему нужно взять только какие-то конкретные выжимки. На этом этапе ИИ прям обработав все это сделала то, что она называет "дистилляция" и сформировала набор &lt;code&gt;.mdc&lt;/code&gt; файлов о том, как же надо программировать. Попутно сгруппировав эту информацию по темам.&lt;/p&gt;

&lt;p&gt;Что в итоге вышло&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;rules&lt;/strong&gt; (&lt;code&gt;.mdc&lt;/code&gt;) — дистилляция Gforth manual, Brodie, Rosetta, theForthNet;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;eval&lt;/strong&gt; — &lt;strong&gt;151&lt;/strong&gt; challenge (&lt;strong&gt;98&lt;/strong&gt; train / &lt;strong&gt;53&lt;/strong&gt; hold-out blind);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gforth&lt;/strong&gt; — судья: каждый gold solution помечен как &lt;code&gt;TESTS OK&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Получилась библиотека - база знаний, которую можно установить (с помощью скрипта) в свой проект на Форте, и если работать с ИИ, то он все эти файлы с правилами будет использовать в работе.&lt;/p&gt;

&lt;p&gt;Cursor подхватывает rules из &lt;code&gt;.cursor/rules/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Но в принципе, я думаю, что можно и самому почитать и что-то понять. Тут сложно всё это оценить. Валидировать всё это одному — долго.&lt;/p&gt;




&lt;h2&gt;
  
  
  Обучение своей модели - Track A
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flefpz2iqj4cfdx1n1xgf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flefpz2iqj4cfdx1n1xgf.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Предпосылки. Перед тем, как начать "по настоящему" обучать модель, именно данными, именно на GPU - мной на работе был для себя открыт RAG как источник данных для модели. То есть, мы берем модель общего назначения и в качестве внешнего источника - подключаем доступ к векторной базе. Мы там получили модель данных о том, как организовано наше производство, и могли задавать вопросы и получать ответы. Создание векторной БД оказалось отдельным видом искусства. Я со стороны наблюдал, как ИИ само пишет скрипты для того, чтобы "правильно" разложить данные в векторы. Потом я задавал какие-то вопросы и ИИ отвечала не так, как я хочу. И далее программирующая ИИ исправляла индексы. На тот момент вообще было слабое понимание, как это все происходит "под капотом". Но я пришел к пониманию того, что по некоторым моим рабочим вопросам мне ИИ вообще не нужно. Мне нужно просто сформировать некий инструментарий, который данные добудет, обработает и выдаст. Это знание мне пригодится немного позже...&lt;/p&gt;

&lt;p&gt;Предположив, что RAG это не совсем то, что я хочу - я захотел освоить пайплайн дообучения модели своими данными. Тот самый случай, когда каким-то образом готовятся датасеты. Потом ими обучают нейросеть и на ней вырастает дополнительный слой знаний и у нас модель "бац" и научилась программировать на Forth! Круто (думал я). В моем понимании на тот момент было то, что Модель+RAG это когда у нас постоянно меняющиеся знания. Мы легко и непринужденно меняем векторную БД и нам не надо переобучать модель. А вот если нам нужно "запечь" знания в модель - вот тогда мы ее и обучаем.&lt;/p&gt;

&lt;p&gt;Чтобы не состариться раньше, чем будет получен результат, я попросил ИИ создать мне как раз этот Track A, где мы возьмем самую маленькую модель 0.5B дообучим ее, увидим то, что она научилась, поймем пайплайн сбора датасета, запуска обучения и выходной результат. И уже после этого пойдем дальше на большие модели, которые будем обучать дольше по времени.&lt;/p&gt;

&lt;p&gt;Чтож. На моем ноутбуке используется 4070 на 8 Гб и в принципе обучение происходит достаточно быстро: буквально несколько минут. Ведь модель небольшая. Что я в итоге получил? Получил сеть, которая вместо Forth кода выдает какую-то многократно повторяющуюся ерунду. После чего мы обучили сеть подольше - добавили повторения, я начал всматриваться в цифры и мне показалось, что я начал что-то понимать: эпохи, процент ошибок. Этот процент начинал снижаться. Но итоговая модель все равно вместо Forth выдала смесь кода на Python и слов Forth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t8kk9vufomw6hrcd9l0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t8kk9vufomw6hrcd9l0.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Дальше мы пошли обсуждать с ИИ полученный результат и как его улучшить. Мне захотелось, чтобы сеть выдавала что-то более полезное. Дать ей больше данных. Но тут мне напомнили, что я, в общем-то, и не должен был получить нормальный результат, потому что целью было - освоить инструментальный пайплайн! Блин. Однако, увидеть, что модель чему-то научилась - у меня вышло. В этот момент надо было закрывать Track A, выкидывать первый блин комом и идти дальше (не пытаясь получить от малой модели что-то осмысленное). Но... Мой ИИ мне сказал, что в моих скриптах нашлась ошибка: данные были обрезаны и модель обучалась не тем, чем нужно. Хорошо - исправляем! Обучили модель уже нормальными данными. И о чудо: модель ... не научилась программировать на Forth, но прогресс был! Она уже начала оформлять результат в виде синтаксически верных определений Forth &lt;code&gt;: forth-word commands ;&lt;/code&gt; и я даже немного обрадовался. &lt;/p&gt;

&lt;p&gt;Сам этап Track А решено было закрыть и дальше не дообучать модель на 0.5B. Почему? Потому что цель - "песочница" выполнена, а мечта «модель 0.5B выучит Forth» — нет. Оставалось понять почему —  это не мало эпох, повторений или данных. Это что-то фундаментальное, чего я еще не понял.&lt;/p&gt;

&lt;h2&gt;
  
  
  Время собирать камни
&lt;/h2&gt;

&lt;p&gt;Накопилось много вопросов, и без ответов на эти вопросы двигаться дальше было никак нельзя. Начались дикие душевные порывы: купить свой мощный компьютер и видеоплату (а какую?), а может в институт сходить, там кластер из 4хA100, или может арендовать облако. Но для чего?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdi14swh63fdryg5oapd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdi14swh63fdryg5oapd.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;В процессе решения задач, ИИ выдает очень много терминов из области ML. Я их собрал и начал обсуждения. Кроме этого, проведя эксперименты и замеры, нужно ведь правильно трактовать результат, уметь делать выводы, а я их делать не умею. Поэтому у нас начался длительный этап общения с целью понять, а что же мы сделали, какой из этого сделать вывод и что делать дальше. И вот мы с ней общались и общались, у меня появлялись новые вопросы, появлялись новые термины. Я по ним задавал дополнительные вопросы. Это хороший механизм узнать то, что ты сам не знаешь (мы не можем знать то, чего не знаем). Постепенно раскрутили диалог.&lt;/p&gt;

&lt;p&gt;Некоторые вопросы я задавал неоднократно, возвращаясь по много раз. Видимо, мне просто не хотелось верить в некоторые свои заблуждения.&lt;/p&gt;

&lt;p&gt;Маленькую модель с самого начала планировалось использовать только для отладки пайплайна обучения и освоения инструментария. Но увидев первые результаты, я все забыл и захотел улучшить работу сети, чтобы получить верный результат - код на Forth. И я упорно пытался найти варианты решений этого вопроса.&lt;/p&gt;

&lt;h3&gt;
  
  
  Стадии принятия тщетности идеи созать свою сеть, программирующую на Forth
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dfsm9em2njtkk6kxn1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dfsm9em2njtkk6kxn1g.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ИИ: &lt;em&gt;Модель 0.5B Слишком мала и ее не обучить. С таким объемом она вообще не может практически ничего. И результат на Forth она не выдаст и выдать не может. Даже топовые облачные модели работают с трудом.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Пока я дошел до приниятия этой мысли, произошла еще целая цепочка познаний и осознаний. Наверняка не все из них верные.&lt;/p&gt;

&lt;p&gt;Осознание +1: RAG - это просто текст в промпт. То есть, нету никакой прямой связи нейросети и векторной БД. Сеть никуда через отдельную дырку не ходит. Все данные из RAG просто текстом вставляются вместе с моим изначальным вопросом в промпт. Всё это попадает в контекст. Модель это понимает и начинает в соответствии с этим отвечать. &lt;/p&gt;

&lt;p&gt;Знание +1: А вот дообучение LoRA - это уже привычки в весах. Но база-сеть та же, но над ней адаптер. Можно выполнить операцию merge и будет новая модель. Но как оказалось, на моем ПК это нереально долго. Я могу быть не прав, но merge упирается в процессор и диск, и GPU тут вообще никак не помогает. В общем, это два разных метода добавить знаний сети. Но в случае с RAG - тратится контекст, а в случае с LoRA нет.&lt;/p&gt;

&lt;p&gt;Осознание +1: LoRA не заменяет RAG/rules в контексте. А контекст - это только то, что существует - факты, тезисы. Но в целом, ответ от наличия RAG может быть лучше, чем от обучения.&lt;/p&gt;

&lt;p&gt;Дискутируя с ИИ про то, как обучать модель и зачем -  появилось частичное понимание, почему не стоит долго обучать модель одними данными, максимально снижая ошибку. Если обучать долго, то ответ будет "зазубриваться", а нужно получить ответ через рассуждение. Именно поэтому не на все задачи в датасете есть ответы. Это как раз для того, чтобы мы научили сеть через задача-ответ, а потом уже даем новую задачу, и ожидаем ответ через рассуждения. Но решение от сети ждем самостоятельное. Далее, имея её рассуждение - ответ, мы можем его валидировать, потому что у нас имеется правильный ответ на задачу. Но увы. Похоже, что размера сети 0.5В не хватит даже на то чтобы зазубрить ответы.&lt;/p&gt;

&lt;p&gt;Знание +1: Hold-out («оставлено не решенным») — задачи, которые не попадали в обучение (ни в challenge-train.jsonl, ни в train-merged для этого slug). Их используют только чтобы проверить модель вслепую, а не «насколько она зазубрила учебник».&lt;/p&gt;

&lt;p&gt;А что вообще можно ждать от 0.5B сети? И что вообще в целом ждать от сети? как она работает? Мы же пользуемся сетями и это какая то магия. А понимание, как они работают - высшая магия. Но хотя бы понять, что и как она отвечает.&lt;/p&gt;

&lt;p&gt;Знание +1: сеть - это вообще предсказатель следующего правильного токена исходя из текущего контекста. &lt;/p&gt;

&lt;p&gt;Что? В смысле?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64m9p3ry1zpgyott9vr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64m9p3ry1zpgyott9vr8.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Осознание +1: осознание того, как думает ИИ и что она может выдавать в ответ, какую задачу она может решить, а какую нет - один из ключевых моментов для получения инструмента разработчика.&lt;/p&gt;

&lt;p&gt;И вот тут я начинаю догадываться, что иду вообще не туда. Я то хочу получить от нейронки прямую генерацию кода на Forth! Но требовать от сети генерацию обратной польской нотации (RPN (1 2 +)) - напрямую это ошибка. Она ни в силу малого объема обучающих данных, ни в силу своей природы - последовательно генерировать слова - вообще не может отменить свои слова и переставить их (без применения внешнего инструмента).&lt;/p&gt;

&lt;p&gt;Осознание +1: сервисы типа cursor - просто создают набор инструментов вокруг думающей модели. Причем текстовых, потому что другого способа связи нет. У модели один примитив: &lt;strong&gt;вопрос → ответ&lt;/strong&gt;. То, что в UI выглядит как Agent с «размышлениями», — &lt;strong&gt;Cursor&lt;/strong&gt; под капотом: это просто несколько скиллов и инструментов mcp.&lt;/p&gt;

&lt;p&gt;Но мне по прежнему не дает покоя мысль. А зачем тогда существуют такие модели по 0.5B? &lt;/p&gt;

&lt;p&gt;Нужно помнить, что никакая сеть не запускает forth в голове, не представляет себе стек и не имеет его в памяти. Она может пользоваться контекстом как блокнотом, в общем, как и человек. Но нужно на много больший объем сети. Маленькие модели могут делать какие-то очень простые вещи, типа автодополнения.&lt;/p&gt;

&lt;p&gt;Попутно выяснилось, что еще и размер модели фиксирован. LoRA не «вырастит» 0.5B до других размеров, если добавлять данных. Сеть всегда остается такого же размера.&lt;/p&gt;

&lt;p&gt;Дальше я пытался понять, а чему же мы тогда учили сеть? Например, я учил сеть определению слов: факториал, фибоначи итд. Видел как меняется loss. Он снижается, казалось бы, можно поучить подольше и все будет хорошо. Задавал вопросы, на что это влияет. Но в итоге мне рассказали, что при снижении loss до 1 - это нормальное обучение, а меньше уже зазубривание. Да и вообще нет правильного ответа на вопрос - при каком loss надо заканчивать. Если обучить до очень низкой ошибки, то просто на запрос fib придет ответ точный, зазубренный. Как если бы у школьника был объем знаний 10 лет русского языка, но ему еще дали зазубрить какие-то слова на английском. Ну он их зазубрит, и только их сможет сказать. Но другого ничего сделать не сможет.&lt;/p&gt;

&lt;p&gt;Вообще я попробовал понять, а чем нейросеть, как дерево, отличается от дерева слов и понятий Forth? Модель же может понимать и классифицировать любую часть Forth выражения. Каждое отдельное слово - это отдельное понятие. Может стоит сначала обучить простым стековым операциям, сложение там итд. А то получается, что я базовым вещам модель не обучил, а сразу прошу решать сложные задачи на форте. Может она поэтому не решает?&lt;/p&gt;

&lt;p&gt;Знание +1: из модели обученной под Python миллионами примеров не сделать модель, которая будет писать на Forth на базе 100 примеров.&lt;/p&gt;

&lt;p&gt;Знание +1: вообще, просить и ожидать от сети точные формулировки - это ошибка. Сама природа сети в том, что она ошибается в мелких деталях и синтаксисе и прям точно ошибется в синтаксисе форта. Чтобы модели генерили json их очень долго и упорно учат. Научить модель с нуля - у домашнего пользователя нету таких ресурсов (ни технических, на научно-датасетовых)&lt;/p&gt;

&lt;p&gt;Но чего же удалось добиться? Я сначала получил вообще очень странный ответ на малой модели. Потом стал дообучать. Увидел выше указанные снижения loss, но лучше не стало. А потом вообще выяснилось, что был рассинхрон промптов. То есть ошибка в скриптах данных на обучении. После исправления, в итоге получились ответы похожие на правду что-то. Но только именно что похожие: &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Слово&lt;/th&gt;
&lt;th&gt;Модель&lt;/th&gt;
&lt;th&gt;Эталон (tests/ans/)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gcd&lt;/td&gt;
&lt;td&gt;begin dup 0= … while dup 0= … dup 1 swap gcd&lt;/td&gt;
&lt;td&gt;begin dup while tuck mod repeat drop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;factorial&lt;/td&gt;
&lt;td&gt;0= if drop exit then 1* factorial&lt;/td&gt;
&lt;td&gt;dup 1&amp;lt;= if drop 1 exit then dup 1- recurse *&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;divisible?&lt;/td&gt;
&lt;td&gt;1 div 0= if 1 else 0&lt;/td&gt;
&lt;td&gt;mod 0=&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Само пределение слова было обернуто в правильный формат  &lt;code&gt;: word ( … ) … ;&lt;/code&gt; есть — это уже не markdown/return формат ответа, как было. Но логика неверна на всех трёх. При проверке gforth справедливо падает на тестах.&lt;/p&gt;

&lt;p&gt;Модель не копирует строки из jsonl (gcd в train есть) — 0.5B не удерживает алгоритм даже при корректном SFT.&lt;/p&gt;

&lt;p&gt;Но smoke тест писал «OK», потому что он проверял наличие ":" в начале определения слова и ";" в конце. Он не сравнивает с эталоном и не требует TESTS OK. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5beugub9vv7fnq6s5xub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5beugub9vv7fnq6s5xub.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;В train не было базовых основ по возрастанию сложности (curriculum), а сразу шли определения сложных задач (gcd/factorial). Таблица выше и есть итог: даже честный SFT не спас. По факту Track A закрыли окончательно.&lt;/p&gt;

&lt;p&gt;Но в голове всё же крутилась идея о том, что логически дерево форта и дерево мышления нейросети очень похожи и генерировать Форт код для нейросети - как будто бы просто записать путь классификации того, что ты осознал. Но я не могу объяснить, как это выразить. И не понимаю, можно ли это понимание применить к результату.&lt;/p&gt;

&lt;p&gt;Раз за разом мы обсуждали эту идею и каждый раз ИИ мне объясняла, что мне так не сделать. И что лучше получить от ИИ - нормально описанный алгоритм, в виде кода на известном ИИ языке программирования, или псевдокоде и уже с помощью внешнего преобразования утилитой - получить Форт код! Потому что утилита сделает эту операцию, быстро, дешево и точно. В отличие от нейросети, которая будет делать это путем обработки огромных массивов данных и при этом будет иметь ненулевой шанс ошибиться в какой-нибудь букве. Это все равно, что забивать гвозди микроскопом.&lt;/p&gt;

&lt;p&gt;После этого я сдался. И признал, что надо делать именно так. Тем более, я уже начал догадываться, что прямая работа с синтаксисом и нотацией - это еще не программирование. Нужно ведь еще алгоритмы понимать, структуры данных и иметь опыт к решению задач используя алгоритмы и структуры. На решение алгоритмических задач! И Coder модели как раз на это и обучены и надо использовать результат их работы. И этот результат уже преобразовывать в Forth не нейронным способом.&lt;/p&gt;

&lt;p&gt;А каким способом? Я начал спрашивать, а может быть модели могут выдать AST? Это почти Forth. Прямого ответа на то, что какая-то модель умеет отдавать AST не нашлось, но по мнению ИИ, при обучении, модели видели миллионы деревьев из учебников по компиляторам. Но чтобы получит AST утверждается, что программисты просят показать ответ в виде List - S-выражений. &lt;/p&gt;

&lt;p&gt;Задача: 1 + (2 * 3)&lt;/p&gt;

&lt;p&gt;AST (в формате Lisp, который отлично генерируют модели): (+ 1 (* 2 3))&lt;/p&gt;

&lt;p&gt;Результат скрипта преобразования в Forth: 1 2 3 * +&lt;/p&gt;

&lt;p&gt;Вообще Форт и Лисп - зеркальны. Их можно выражать один из другого.&lt;/p&gt;

&lt;p&gt;Какие еще варианты? Ассемблер? Не совсем то. Ассемблер для стековой виртуальной машины? Да!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Вот стековые языки, которые LLM знают на 10 из 10:
WebAssembly (WASM): В текстовом виде (.wat) WASM — это чистая стековая машина.
Байт-код Java (JVM): Вычисления в JVM происходят строго на стеке операндов.
PostScript (PS): Язык принтеров PDF. А теперь шок-контент: PostScript — это диалект Форта! Он использует ту же Обратную Польскую Запись, те же слова dup, pop (аналог drop), exch (аналог swap).
CIL (Байт-код .NET) и Байт-код Python (dis) — тоже стековые.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Далее я прихожу к выводу о том, что итоговый Forth результат можно получить вообще другим путем - через внешний инструмент преобразования. В составе набора инструментов вокруг ИИ модели - так называемого "завода".&lt;/p&gt;

&lt;p&gt;Остановиться решил на WASM&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Смотри, как поразительно WASM похож на Форт:
Код на WASM (модель пишет его без ошибок):

get_local $a
get_local $b
i32.add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Далее мы тупо заменяем и получаем Forth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkze7yz705vxea4ou68kq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkze7yz705vxea4ou68kq.png" alt=" " width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Чтож, подобьем еще раз: &lt;/p&gt;

&lt;p&gt;Мой тезис: «Forth должен быть простым для дерева/нейросети»&lt;/p&gt;

&lt;p&gt;Ответ ИИ: Forth прост для компилятора, не для autoregressive LLM:&lt;/p&gt;

&lt;p&gt;LLM генерирует слева направо; RPN требует «держать операцию в голове», пока кладёшь операнды. Forth смешивает данные и метапрограммирование (immediate, ['], create/does&amp;gt;) — трансформер этого не «исполняет», только имитирует текст. Поэтому ваша цепочка «разобрать задачу → записать токенами» правильная — но середина (разбор → RPN) должна быть алгоритмом, а сетью, и тем более не сетью на 0.5B.&lt;/p&gt;

&lt;p&gt;После этого мне захотелось четко разграничить "инструмент-задача" и какие из моих задач надо решать нейросетями, а какие - инструментами экосистемы "завода".&lt;/p&gt;

&lt;p&gt;Чего не стоит ждать от ИИ (архитектурные ограничения):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Исполнять код, держать стек в «голове», откаты при ошибке — нет внутренней VM; только текст.&lt;/li&gt;
&lt;li&gt;Гарантированная корректность — никогда; только вероятность.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Потом мы немного поговорили про то, что кроме дообучения модели, есть еще и обучение модели вообще с нуля (не переучивать с Python на Forth, а учить на Forth с нуля). И что этот процесс для домашней разработки вообще нежизнеспособен. Нужно научить делать все уровни программирования. Но даже если иметь много денег и специалистов - ошибочна сама по себе идея делать текст на Forth. Нейросеть все равно будет ошибаться, а если нет, то это забивание гвоздей микроскопом.&lt;/p&gt;

&lt;p&gt;Но что мы можем сделать? Мы можем сделать "Завод" из тулчейна!&lt;/p&gt;

&lt;p&gt;Ну и далее, выходит, что мы можем получить от большой модели саму суть алгоритма и код в удобной для трансляции в Форт нотации. Инструментально преобразовать. Инструментально проверить. Дать ответ. Тем самым мы избавим большую модель через боль, страдания и большие затраты токенов, программировать на Forth.&lt;/p&gt;

&lt;p&gt;Так что задача сильно поменялась с точки зрения философии решения: ИИ не должен писать на Forth сам, а должен с помощью внешних инструментов получить форт и проверить результат. И для валидации преобразования как раз пригодятся написанные за несколько дней инструменты для Forth экосистемы: fmix + test, lint flint, fcov coverage.&lt;/p&gt;

&lt;p&gt;Если все получится, то мы сможем получать Forth код, но обходиться это будет дешевле. Если cursor+Opus запускать над этим tools функционалом, то результат должен получаться дешевле.&lt;/p&gt;

&lt;p&gt;Таким образом, мы пришли к пониманию и изобрели (велосипед) такое понятие, которое все называют skills/tools.&lt;/p&gt;

&lt;p&gt;Осознание: монолитных ИИ нейронок, которые умеют программировать - нет. Мы ими не пользуемся. Мы пользуемся  ИИ экосистемами, которые умеют в итоге получить программный код. А дома мы запускаем одну моно модель. И потом удивляемся - я че она такая тупая. Но мы сравниваем одну маленькую модель с большим набором больших моделей и инструментов вокруг них.&lt;/p&gt;

&lt;h3&gt;
  
  
  Терминология ИИ
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5oie7t8nt0towpiojr0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5oie7t8nt0towpiojr0y.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Термины, которые ИИ активно использовала, объясняя мне принципы своей работы. Вообще в этом есть забавный момент: мы с ИИ обсуждаем ИИ )) Я их дам прямо так, как их мне объяснила ИИ. В рамках контекста нашего с ней диалога. Я думаю, лучше по каждому слову искать в интернете более корректное объяснение. Тут ценен именно сам перечень слов.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Термин&lt;/th&gt;
&lt;th&gt;По-простому&lt;/th&gt;
&lt;th&gt;Что происходит технически&lt;/th&gt;
&lt;th&gt;Для Forth / frules&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Параметр (parameter)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;«Ячейка памяти» сети&lt;/td&gt;
&lt;td&gt;Одно обучаемое число в матрице&lt;/td&gt;
&lt;td&gt;0.5B = 500 млн таких чисел&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0.5B / 7B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Размер / ёмкость модели&lt;/td&gt;
&lt;td&gt;Число параметров; больше → больше паттернов&lt;/td&gt;
&lt;td&gt;0.5B — мало для алгоритмов на стеке&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pretrain (претрейн)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;«Школа жизни» на интернете&lt;/td&gt;
&lt;td&gt;Predict next token на огромном корпусе&lt;/td&gt;
&lt;td&gt;Forth там ≈ 0%; зато Python, C, «код вообще» в достатке&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Token (токен)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Кусок текста (~слово/часть)&lt;/td&gt;
&lt;td&gt;Единица входа/выхода LLM&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;: gcd&lt;/code&gt; может быть 2–3 токена&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transformer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Архитектура LLM&lt;/td&gt;
&lt;td&gt;Attention: каждый токен «смотрит» на предыдущие&lt;/td&gt;
&lt;td&gt;Нет стека, нет VM — только текст&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SFT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supervised fine-tuning&lt;/td&gt;
&lt;td&gt;Учебник: prompt → эталонный ответ&lt;/td&gt;
&lt;td&gt;Ваш &lt;code&gt;train-merged.jsonl&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Instruct / instruction-tuned&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Модель «ассистент»&lt;/td&gt;
&lt;td&gt;SFT на диалогах «вопрос → ответ»&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Qwen2.5-Coder-0.5B-**Instruct**&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Тонкая «накладка»&lt;/td&gt;
&lt;td&gt;Мало новых весов; база заморожена (QLoRA: в 4-bit)&lt;/td&gt;
&lt;td&gt;~17 MB поверх 0.5B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QLoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LoRA + сжатая база&lt;/td&gt;
&lt;td&gt;База в 4-bit, учится только adapter&lt;/td&gt;
&lt;td&gt;Track A на RTX 4070&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loss (потери)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ошибка угадывания&lt;/td&gt;
&lt;td&gt;Cross-entropy: насколько модель промахнулась по следующему токену&lt;/td&gt;
&lt;td&gt;Низкий loss ≠ рабочий Forth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Epoch (эпоха)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Один проход по всему jsonl&lt;/td&gt;
&lt;td&gt;Сколько раз модель видела каждую строку&lt;/td&gt;
&lt;td&gt;3 ep на 139 строк — мало для обобщения&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overfit / зазубривание&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Запомнила train, не переносит&lt;/td&gt;
&lt;td&gt;Отлично на тех же промптах, плохо на новых&lt;/td&gt;
&lt;td&gt;Старый run: зазубрила &lt;strong&gt;rules&lt;/strong&gt;, не gcd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Generalization (обобщение)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Перенос на новое&lt;/td&gt;
&lt;td&gt;Работает на промптах/задачах вне train&lt;/td&gt;
&lt;td&gt;0.5B + Forth: practically нет&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memorization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Копирование train-пар&lt;/td&gt;
&lt;td&gt;Ответ ≈ строка из jsonl&lt;/td&gt;
&lt;td&gt;Даже этого 0.5B не сделала для gcd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Curriculum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;От простого к сложному&lt;/td&gt;
&lt;td&gt;Сначала &lt;code&gt;1 2 +&lt;/code&gt;, потом &lt;code&gt;: square&lt;/code&gt;, потом gcd&lt;/td&gt;
&lt;td&gt;В frules не было; нужно для маленьких моделей&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hold-out&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Задачи не из train&lt;/td&gt;
&lt;td&gt;Только для проверки&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;eval_holdout&lt;/code&gt; в &lt;code&gt;tests/challenges/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;System prompt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Роль / правила в чате&lt;/td&gt;
&lt;td&gt;Первое сообщение &lt;code&gt;role: system&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;SFT: short (~50 tok); Ollama: full rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reasoning (рассуждение)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Цепочка шагов к ответу&lt;/td&gt;
&lt;td&gt;Модель генерирует промежуточные шаги (CoT)&lt;/td&gt;
&lt;td&gt;Forth-стек = нужен «scratchpad» или алгоритм&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CoT (chain-of-thought)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;«Думай вслух» в тексте&lt;/td&gt;
&lt;td&gt;В ответе: стек, разбор, потом код&lt;/td&gt;
&lt;td&gt;Помогает &lt;strong&gt;большим&lt;/strong&gt; моделям; 0.5B ломается на длинной цепочке&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rules (frules)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Текст в промпте, не в весах&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.mdc&lt;/code&gt; в SYSTEM; веса не меняются&lt;/td&gt;
&lt;td&gt;Cursor / Ollama — сильнее LoRA 0.5B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merge LoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Влить adapter в базу&lt;/td&gt;
&lt;td&gt;Полные веса для Ollama/GGUF&lt;/td&gt;
&lt;td&gt;Отдельно от качества Forth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GGUF / Ollama&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Формат локального inference&lt;/td&gt;
&lt;td&gt;Квантованная модель для чата&lt;/td&gt;
&lt;td&gt;Rules в Modelfile ≠ train jsonl&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Побочные продукты этой увлекательной недели
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1hvrps1zwwlc307k3pz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1hvrps1zwwlc307k3pz.png" alt=" " width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Пока я всем этим занимался, попутно создавались некоторые инструменты. В итоге, мне захотелось попробовать оценить объемы того, что получилось: по периоду времени, объему функционалу и объему кода.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repo&lt;/th&gt;
&lt;th&gt;Период&lt;/th&gt;
&lt;th&gt;Версии&lt;/th&gt;
&lt;th&gt;Одной строкой&lt;/th&gt;
&lt;th&gt;Код*&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fmix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2024 → 24.05&lt;/td&gt;
&lt;td&gt;0.7.x&lt;/td&gt;
&lt;td&gt;Пакетный менеджер&lt;/td&gt;
&lt;td&gt;~1.2k LOC &lt;code&gt;.4th&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fsemver&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24.05, 1 день&lt;/td&gt;
&lt;td&gt;0.1.x&lt;/td&gt;
&lt;td&gt;Semver-библиотека (для fmix/flint)&lt;/td&gt;
&lt;td&gt;~360 LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fcov&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24.05, 1 день&lt;/td&gt;
&lt;td&gt;0.1→&lt;strong&gt;0.3&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Инструмент Coverage: console/JSON/LCOV/HTML&lt;/td&gt;
&lt;td&gt;~2.8k LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;flint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24.05, 1 день&lt;/td&gt;
&lt;td&gt;0.1→&lt;strong&gt;0.2.2&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Lint дубликатов &lt;code&gt;: word&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;~825 LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fenum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;22.05&lt;/td&gt;
&lt;td&gt;0.1.x&lt;/td&gt;
&lt;td&gt;Библиотека для &lt;code&gt;ulist&lt;/code&gt; и &lt;code&gt;enum&lt;/code&gt; (используется в flint/fcov)&lt;/td&gt;
&lt;td&gt;~750 LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fhdlgen&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20–24.05&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;Преобразователь DSL→Verilog (расскажу в следующий раз)&lt;/td&gt;
&lt;td&gt;~2k LOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;frules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25–31.05&lt;/td&gt;
&lt;td&gt;0.1.x&lt;/td&gt;
&lt;td&gt;151/98/53, Track A closed, docs hub&lt;/td&gt;
&lt;td&gt;gold ~6.5k; rules ~2.1k md&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;*LOC без &lt;code&gt;forth-packages/&lt;/code&gt; — просто чтоб оценить объемы.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;24 мая&lt;/strong&gt; — flint, fcov, fsemver были написаны всего за один день с помощью Opus 4.8. &lt;strong&gt;frules&lt;/strong&gt; — ещё шесть дней: challenge bank, gold, первые проверки  (Track A), hub с документацией по Forth.&lt;/p&gt;

&lt;p&gt;Обычно меня душит жаба, за бесцельно потраченные деньги. Но здесь — нет: ни денег, ни бессонных ночей не жалею. Слишком много узнал и осознал за эти дни. Многие навыки пригодились и на основной работе&lt;/p&gt;

&lt;h2&gt;
  
  
  Коротко: антипаттерны
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;«Opus напишет весь код &lt;code&gt;: word ... ;&lt;/code&gt;» — overhard + overkill + overinvoice.&lt;/li&gt;
&lt;li&gt;«LoRA научится postfix» — нет (Track A закрыт).&lt;/li&gt;
&lt;li&gt;Ждать от 0.5B «завода» — получите стажёра, не конвейер.&lt;/li&gt;
&lt;li&gt;Генерировать Forth напрямую, когда нужен IR → transpiler.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcway3gsbrejm1jthhe8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcway3gsbrejm1jthhe8.png" alt=" " width="798" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Пять оплаченных инвойсов за неделю (23–29 мая): $75.06 + $50.85 + $34.66 + $75.00 + $27.84 ≈ *&lt;/em&gt;$263**. Счёт ползёт вместе с &lt;code&gt;rot&lt;/code&gt; и thinking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72olrggs0hetflxz4zz2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72olrggs0hetflxz4zz2.gif" alt=" " width="560" height="996"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Чему мы научились?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02yvq28co10hhpcf10bp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02yvq28co10hhpcf10bp.png" alt=" " width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Источники
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;VitaSound:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/frules" rel="noopener noreferrer"&gt;frules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/feco" rel="noopener noreferrer"&gt;feco&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/fmix" rel="noopener noreferrer"&gt;fmix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/fmcp" rel="noopener noreferrer"&gt;fmcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/flint" rel="noopener noreferrer"&gt;flint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/fcov" rel="noopener noreferrer"&gt;fcov&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/VitaSound/fsemver" rel="noopener noreferrer"&gt;fsemver&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Продолжение следует ...&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>forth</category>
      <category>toochain</category>
    </item>
    <item>
      <title>FMix: пакетный менеджер для Forth</title>
      <dc:creator>Alexey Bolshakov</dc:creator>
      <pubDate>Mon, 18 May 2026 23:12:43 +0000</pubDate>
      <link>https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p</link>
      <guid>https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf2rgkhcmxfwn1m9zoy4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf2rgkhcmxfwn1m9zoy4.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;В современных языках программирования мы почти всегда ожидаем, что рядом с языком будет удобный инструмент для работы с проектом.&lt;/p&gt;

&lt;p&gt;В Elixir есть &lt;a href="https://hexdocs.pm/mix/Mix.html" rel="noopener noreferrer"&gt;Mix&lt;/a&gt;. В Rust есть &lt;a href="https://doc.rust-lang.org/cargo/" rel="noopener noreferrer"&gt;Cargo&lt;/a&gt;. В Ruby есть &lt;a href="https://bundler.io/" rel="noopener noreferrer"&gt;Bundler&lt;/a&gt; и &lt;a href="https://rubygems.org/" rel="noopener noreferrer"&gt;RubyGems&lt;/a&gt;. В Haskell есть &lt;a href="https://www.haskell.org/cabal/" rel="noopener noreferrer"&gt;Cabal&lt;/a&gt; и &lt;a href="https://docs.haskellstack.org/" rel="noopener noreferrer"&gt;Stack&lt;/a&gt;. В JavaScript есть &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;npm&lt;/a&gt;, &lt;a href="https://pnpm.io/" rel="noopener noreferrer"&gt;pnpm&lt;/a&gt; и &lt;a href="https://yarnpkg.com/" rel="noopener noreferrer"&gt;Yarn&lt;/a&gt;. В Go есть &lt;a href="https://go.dev/ref/mod" rel="noopener noreferrer"&gt;modules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Мы привыкли, что можно создать проект одной командой, описать зависимости в одном файле, подтянуть их из сети, запустить тесты, собрать релиз. Это кажется базовой частью экосистемы, а не отдельной роскошью.&lt;/p&gt;

&lt;p&gt;Но у старых языков программирования часто другая история.&lt;/p&gt;

&lt;p&gt;Когда многие из них создавались, такой подход ещё не был стандартом. Код распространялся иначе, проекты были устроены иначе, а экосистемы развивались без привычных сегодня package managers и build tools.&lt;/p&gt;

&lt;p&gt;И это до сих пор чувствуется.&lt;/p&gt;

&lt;h2&gt;
  
  
  Почему я задумался об этом
&lt;/h2&gt;

&lt;p&gt;Для одного из своих проектов я использую &lt;a href="https://forth-standard.org/" rel="noopener noreferrer"&gt;Forth&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Forth — очень интересный язык. Минималистичный, прямой, необычный. В нём есть что-то очень привлекательное: ты находишься близко к вычислениям, близко к стеку, близко к самому железу вычислительной машины (которую иногда я делаю сам).&lt;/p&gt;

&lt;p&gt;Но когда я начал писать на Forth что-то более-менее похожее на проект, мне быстро стало не хватать привычных вещей:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;создать новый проект;&lt;/li&gt;
&lt;li&gt;описать зависимости;&lt;/li&gt;
&lt;li&gt;скачать зависимости;&lt;/li&gt;
&lt;li&gt;запускать тесты;&lt;/li&gt;
&lt;li&gt;иметь понятную структуру проекта;&lt;/li&gt;
&lt;li&gt;подключать библиотеки из Git;&lt;/li&gt;
&lt;li&gt;не держать всё вручную в голове.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Да, можно использовать git submodules, можно копировать файлы руками, можно придумывать свои соглашения. Но для меня это не очень удобный путь. Я хотел что-то ближе к тому, к чему привык в современных языках.&lt;/p&gt;

&lt;p&gt;Я нашёл библиотеку &lt;code&gt;f.4th&lt;/code&gt;, связанную с &lt;a href="https://theforth.net/" rel="noopener noreferrer"&gt;theforth.net&lt;/a&gt;. Это полезный инструмент, но у него другой подход: библиотеки и версии хранятся на своём сервере.&lt;/p&gt;

&lt;p&gt;Мне же хотелось уметь подтягивать зависимости прямо из Git-репозиториев. Например, указать GitHub URL, ветку или тег, и получить библиотеку в проект.&lt;/p&gt;

&lt;p&gt;Так появилась идея сделать &lt;code&gt;fmix&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Что такое FMix
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fmix&lt;/code&gt; — это небольшой build tool и package tool для Forth, вдохновлённый Elixir Mix.&lt;/p&gt;

&lt;p&gt;Репозиторий: &lt;a href="https://github.com/VitaSound/fmix" rel="noopener noreferrer"&gt;github.com/VitaSound/fmix&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Он не пытается быть большим фреймворком. Его задача проще: дать Forth-проекту минимальный набор привычных инструментов.&lt;/p&gt;

&lt;p&gt;Сейчас &lt;code&gt;fmix&lt;/code&gt; умеет:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix new example
fmix packages.get
fmix &lt;span class="nb"&gt;test
&lt;/span&gt;fmix version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;То есть можно создать новый пакет, подтянуть зависимости, запустить тесты и посмотреть версию инструмента.&lt;/p&gt;

&lt;h2&gt;
  
  
  Создание проекта
&lt;/h2&gt;

&lt;p&gt;Новый проект создаётся так:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix new example
&lt;span class="nb"&gt;cd &lt;/span&gt;example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;После этого появляется базовая структура проекта:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example/
  example.4th
  package.4th
  README.md
  tests/
  forth-packages/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;package.4th&lt;/code&gt; — это файл описания пакета. В нём указывается имя, версия, лицензия, основной файл и зависимости.&lt;/p&gt;

&lt;p&gt;Пример:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;forth-package
    key-value name example
    key-value version 0.1.0
    key-value license COPL
    key-value description example
    key-value main example.4th

    key-list dependencies f git https://github.com/VitaSound/f tag 0.2.4
    key-list dependencies ttester git https://github.com/VitaSound/ttester tag 1.1.0
end-forth-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Мне нравится, что сам файл тоже написан в стиле Forth. Это не JSON, не YAML и не TOML, а маленький декларативный Forth-формат.&lt;/p&gt;

&lt;h2&gt;
  
  
  Зависимости из Git
&lt;/h2&gt;

&lt;p&gt;Главная причина, по которой я начал делать &lt;code&gt;fmix&lt;/code&gt;, — это зависимости из Git.&lt;/p&gt;

&lt;p&gt;В &lt;code&gt;package.4th&lt;/code&gt; можно указать зависимость так:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key-list dependencies ftest git https://github.com/VitaSound/ftest.git branch main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Или так, если нужна конкретная версия через tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key-list dependencies ftest git https://github.com/VitaSound/ftest.git tag 0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;После этого достаточно выполнить:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix packages.get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Зависимости будут установлены в локальную папку проекта:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./forth-packages/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Структура позволяет хранить разные версии одной библиотеки рядом друг с другом:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;forth-packages/
  f/
    0.2.4/
  ttester/
    1.1.0/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Подключать зависимость можно обычным &lt;code&gt;require&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require ./forth-packages/f/0.2.4/f.4th
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Поддержка theforth.net
&lt;/h2&gt;

&lt;p&gt;Хотя мне хотелось Git-зависимостей, я не хотел полностью отказываться от существующей экосистемы.&lt;/p&gt;

&lt;p&gt;Поэтому &lt;code&gt;fmix&lt;/code&gt; также умеет работать с пакетами из &lt;code&gt;theforth.net&lt;/code&gt; через &lt;code&gt;f.4th&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Например:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key-list dependencies f 0.2.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Таким образом, в одном проекте можно использовать и Git-зависимости, и пакеты из &lt;code&gt;theforth.net&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fweufktv2vbz4o7gwjo9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fweufktv2vbz4o7gwjo9y.png" alt=" " width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Тесты
&lt;/h2&gt;

&lt;p&gt;Ещё одна базовая вещь, которой мне не хватало, — запуск тестов.&lt;/p&gt;

&lt;p&gt;В &lt;code&gt;fmix&lt;/code&gt; есть команда:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Она запускает тесты из директории &lt;code&gt;tests/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Можно запустить и конкретный файл:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix &lt;span class="nb"&gt;test &lt;/span&gt;tests/some_test.4th
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Для тестов используется &lt;code&gt;ttester&lt;/code&gt;. Если &lt;code&gt;ttester&lt;/code&gt; есть в зависимостях проекта, &lt;code&gt;fmix&lt;/code&gt; берёт его из &lt;code&gt;./forth-packages&lt;/code&gt;. Если нет — использует версию из установленного &lt;code&gt;FMIX_HOME&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Для меня это важно: тесты должны запускаться одной командой. Даже если проект маленький.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyyz5hya9p3s0n5amsx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyyz5hya9p3s0n5amsx1.png" alt=" " width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Что было исправлено в последних версиях
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fmix&lt;/code&gt; пережил уже несколько поколений изменений.&lt;/p&gt;

&lt;p&gt;Первые версии я писал сам. Но я не очень сильный Forth-разработчик, поэтому многие вещи давались тяжело. Одну крупную переработку я сделал с помощью Gemini. Последнюю версию я уже доводил с помощью Cursor.&lt;/p&gt;

&lt;p&gt;В последних релизах было исправлено довольно много практических проблем.&lt;/p&gt;

&lt;p&gt;Появился GitHub Actions CI: workflow собирает &lt;a href="https://www.gnu.org/software/gforth/" rel="noopener noreferrer"&gt;GForth&lt;/a&gt; 0.7.9 из исходников и запускает:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gforth fmix.4th &lt;span class="nt"&gt;-e&lt;/span&gt; version
gforth fmix.4th &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Позже появился release pipeline: при push version tag автоматически создаётся GitHub Release, а текст релиза берётся из файла &lt;code&gt;.github/RELEASE_NOTES_X.Y.Z.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Также были исправлены проблемы с путями. Раньше часть логики была завязана на &lt;code&gt;PWD&lt;/code&gt;, теперь проект определяется через текущую директорию запуска. Это важно, потому что &lt;code&gt;FMIX_HOME&lt;/code&gt; указывает на установленный &lt;code&gt;fmix&lt;/code&gt;, а команды должны работать с текущим проектом.&lt;/p&gt;

&lt;p&gt;Были исправлены ошибки при установке зависимостей:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git clone&lt;/code&gt; теперь проверяет результат;&lt;/li&gt;
&lt;li&gt;неуспешные shell-команды завершают GForth с exit code &lt;code&gt;1&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;перед &lt;code&gt;git clone&lt;/code&gt; создаются родительские директории;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git fetch&lt;/code&gt; и &lt;code&gt;checkout&lt;/code&gt; выполняются так, чтобы ошибка не маскировалась;&lt;/li&gt;
&lt;li&gt;для Git-команд используется &lt;code&gt;GIT_TERMINAL_PROMPT=0&lt;/code&gt;, чтобы команда не зависала в non-interactive окружении.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ещё одна важная вещь — валидация входных данных. &lt;code&gt;fmix&lt;/code&gt; проверяет имена пакетов, версии, пути, Git URL и Git refs перед тем, как вызывать &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt; или &lt;code&gt;git&lt;/code&gt;. Это не сложная система escaping, а простой whitelist, но он отсекает пробелы, &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, backticks, pipes и другие shell metacharacters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1g0sbihdd7kuag5sk9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1g0sbihdd7kuag5sk9p.png" alt=" " width="680" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Неожиданная проблема: терминал
&lt;/h2&gt;

&lt;p&gt;Отдельная история была с терминалом. Перед тем, как пользоваться командой &lt;code&gt;fmix&lt;/code&gt; в консоли, ее нужно прописать как alias.&lt;/p&gt;

&lt;p&gt;Так вот при запуске через простой alias вроде:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;fmix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'gforth "$FMIX_HOME/fmix.4th" -e'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;после выполнения команд терминал иногда оставался в плохом состоянии. На prompt появлялись странные символы вроде:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0c0c
[?2004l
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Особенно это проявлялось в WSL.&lt;/p&gt;

&lt;p&gt;В версии &lt;code&gt;0.4.4&lt;/code&gt; я перешёл на launcher script &lt;code&gt;bin/fmix&lt;/code&gt;. Он восстанавливает TTY после команд, сбрасывает bracketed paste mode и немного очищает накопившийся ввод из &lt;code&gt;/dev/tty&lt;/code&gt;. На самом деле все эти вещи я бы точно не разрешил самостоятельно. Я терпеливо помогал нейросети раз за разом пробовать некоторые гипотезы, чтобы исправить эту проблему. В отличие от тех ИИ, что я пробовал раньше, в этот раз я только один раз решил прервать ИИ и создать новый контекст, потому что она как будто зациклилась в бесконечном исправлении.&lt;/p&gt;

&lt;p&gt;Однако, получилось и теперь вместо alias рекомендуется использовать:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FMIX_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/fmix"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FMIX_HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;И запускать просто:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Установка
&lt;/h2&gt;

&lt;p&gt;Сейчас установка выглядит так:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/VitaSound/fmix.git ~/fmix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Затем нужно добавить в shell config (например .bashrc):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FMIX_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/fmix"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FMIX_HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;После этого команда начнет работать прямо в консоли:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Важно: сейчас &lt;code&gt;fmix&lt;/code&gt; рассчитан на Linux, Git, sed, cp и &lt;a href="https://www.gnu.org/software/gforth/" rel="noopener noreferrer"&gt;GForth&lt;/a&gt; 0.7.9.&lt;/p&gt;

&lt;p&gt;Я не рекомендую использовать GForth более ранних версий и версию из &lt;a href="https://snapcraft.io/" rel="noopener noreferrer"&gt;Snap&lt;/a&gt;. Snap запускает программы в изолированном окружении, из-за чего текущая директория и пути могут не совпадать с тем, что ожидает shell session. Это ломает команды вроде &lt;code&gt;new&lt;/code&gt; и &lt;code&gt;packages.get&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Лучше использовать GForth из apt, локальную сборку или tarball с &lt;a href="https://www.gnu.org/software/gforth/" rel="noopener noreferrer"&gt;официальной страницы GForth&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Зачем это всё
&lt;/h2&gt;

&lt;p&gt;Мне кажется, что старым языкам очень нужны такие инструменты, чтобы соответсвовать современным реалиям. &lt;/p&gt;

&lt;p&gt;Не обязательно огромные. Не обязательно идеальные. Но хотя бы такие, которые закрывают базовые сценарии.  Когда у языка есть удобный инструмент вокруг проектов, он становится дружелюбнее.&lt;/p&gt;

&lt;p&gt;Новому человеку проще попробовать язык. Опытному пользователю проще начать новый проект. Библиотеки проще переиспользовать. Тесты проще запускать.&lt;/p&gt;

&lt;p&gt;Это не меняет сам язык, но меняет опыт работы с ним. Позволяет взять старый инструмент в новые проекты.&lt;/p&gt;

&lt;p&gt;В случае Forth это особенно интересно. Forth часто воспринимается как что-то старое, странное и нишевое. Но, возможно, часть этой «старости» находится не только в языке, а в отсутствии привычной современной обвязки.&lt;/p&gt;

&lt;p&gt;Если добавить удобные инструменты, язык может ощущаться совсем иначе.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F87d7e8ac-94fd-4a4a-a9a8-3a32c658714f" class="article-body-image-wrapper"&gt;&lt;img width="720" height="480" alt="programming-languages-personified-fmix" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F87d7e8ac-94fd-4a4a-a9a8-3a32c658714f"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Что дальше
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fmix&lt;/code&gt; пока маленький и экспериментальный.&lt;/p&gt;

&lt;p&gt;Сейчас он умеет создавать пакеты, подтягивать зависимости из Git и &lt;code&gt;theforth.net&lt;/code&gt;, запускать тесты.&lt;/p&gt;

&lt;p&gt;В будущем хочется улучшать совместимость, добавить документацию, и, возможно, добавить больше команд вокруг разработки Forth-пакетов.&lt;/p&gt;

&lt;p&gt;Но уже сейчас мне стало гораздо удобнее работать над своими Forth-проектами. Но это уже совсем другая история&lt;/p&gt;

&lt;h2&gt;
  
  
  Ссылки
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/fmix" rel="noopener noreferrer"&gt;FMix on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/gforth/" rel="noopener noreferrer"&gt;GForth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://theforth.net/" rel="noopener noreferrer"&gt;theforth.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/TG9541/stm8ef" rel="noopener noreferrer"&gt;STM8 eForth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.me/forthchat" rel="noopener noreferrer"&gt;[NF] Forth-like languages, язык Форт&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.me/ruforth" rel="noopener noreferrer"&gt;[TF] Форт и общение фортеров&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>forth</category>
      <category>opensource</category>
      <category>tooling</category>
      <category>ai</category>
    </item>
    <item>
      <title>FMix: a package manager for Forth</title>
      <dc:creator>Alexey Bolshakov</dc:creator>
      <pubDate>Mon, 18 May 2026 23:12:32 +0000</pubDate>
      <link>https://dev.to/ua3mqj/fmix-a-package-manager-for-forth-37ld</link>
      <guid>https://dev.to/ua3mqj/fmix-a-package-manager-for-forth-37ld</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30cyxshvkhfj37m9mioa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30cyxshvkhfj37m9mioa.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In modern programming languages, we almost always expect the language to come with a convenient tool for working with projects.&lt;/p&gt;

&lt;p&gt;Elixir has &lt;a href="https://hexdocs.pm/mix/Mix.html" rel="noopener noreferrer"&gt;Mix&lt;/a&gt;. Rust has &lt;a href="https://doc.rust-lang.org/cargo/" rel="noopener noreferrer"&gt;Cargo&lt;/a&gt;. Ruby has &lt;a href="https://bundler.io/" rel="noopener noreferrer"&gt;Bundler&lt;/a&gt; and &lt;a href="https://rubygems.org/" rel="noopener noreferrer"&gt;RubyGems&lt;/a&gt;. Haskell has &lt;a href="https://www.haskell.org/cabal/" rel="noopener noreferrer"&gt;Cabal&lt;/a&gt; and &lt;a href="https://docs.haskellstack.org/" rel="noopener noreferrer"&gt;Stack&lt;/a&gt;. JavaScript has &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;npm&lt;/a&gt;, &lt;a href="https://pnpm.io/" rel="noopener noreferrer"&gt;pnpm&lt;/a&gt; and &lt;a href="https://yarnpkg.com/" rel="noopener noreferrer"&gt;Yarn&lt;/a&gt;. Go has &lt;a href="https://go.dev/ref/mod" rel="noopener noreferrer"&gt;modules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We are used to being able to create a project with one command, describe dependencies in one file, fetch them from the network, run tests, and prepare a release. It feels like a basic part of the ecosystem, not a luxury.&lt;/p&gt;

&lt;p&gt;But older programming languages often have a different story.&lt;/p&gt;

&lt;p&gt;When many of them were created, this approach was not yet a standard expectation. Code was distributed differently, projects were structured differently, and ecosystems grew without the package managers and build tools we now take for granted.&lt;/p&gt;

&lt;p&gt;And you can still feel it today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Started Thinking About This
&lt;/h2&gt;

&lt;p&gt;For one of my projects I use &lt;a href="https://forth-standard.org/" rel="noopener noreferrer"&gt;Forth&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Forth is a very interesting language. It is minimalistic, direct, and unusual. There is something very attractive about it: you are close to computation, close to the stack, and close to the actual hardware of the computer, which I sometimes build myself.&lt;/p&gt;

&lt;p&gt;But when I started writing something in Forth that looked more or less like a project, I quickly began missing the usual things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a new project;&lt;/li&gt;
&lt;li&gt;describing dependencies;&lt;/li&gt;
&lt;li&gt;downloading dependencies;&lt;/li&gt;
&lt;li&gt;running tests;&lt;/li&gt;
&lt;li&gt;having a clear project structure;&lt;/li&gt;
&lt;li&gt;connecting libraries from Git;&lt;/li&gt;
&lt;li&gt;not having to keep everything manually in my head.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, you can use git submodules, copy files by hand, or invent your own conventions. But for me, this is not a very convenient path. I wanted something closer to what I am used to in modern languages.&lt;/p&gt;

&lt;p&gt;I found the &lt;code&gt;f.4th&lt;/code&gt; library, which is connected to &lt;a href="https://theforth.net/" rel="noopener noreferrer"&gt;theforth.net&lt;/a&gt;. It is a useful tool, but it follows a different approach: library code and versions are stored on its own server.&lt;/p&gt;

&lt;p&gt;I wanted to be able to pull dependencies directly from Git repositories. For example, specify a GitHub URL, a branch or a tag, and get the library into the project.&lt;/p&gt;

&lt;p&gt;That is how the idea for &lt;code&gt;fmix&lt;/code&gt; appeared.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is FMix
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fmix&lt;/code&gt; is a small build tool and package tool for Forth, inspired by Elixir Mix.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/VitaSound/fmix" rel="noopener noreferrer"&gt;github.com/VitaSound/fmix&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It does not try to be a large framework. Its goal is simpler: to give a Forth project a minimal set of familiar tools.&lt;/p&gt;

&lt;p&gt;Right now &lt;code&gt;fmix&lt;/code&gt; can do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix new example
fmix packages.get
fmix &lt;span class="nb"&gt;test
&lt;/span&gt;fmix version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words, you can create a new package, fetch dependencies, run tests, and check the tool version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Project
&lt;/h2&gt;

&lt;p&gt;A new project is created like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix new example
&lt;span class="nb"&gt;cd &lt;/span&gt;example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you get a basic project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example/
  example.4th
  package.4th
  README.md
  tests/
  forth-packages/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;package.4th&lt;/code&gt; is the package description file. It contains the name, version, license, main file, and dependencies.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;forth-package
    key-value name example
    key-value version 0.1.0
    key-value license COPL
    key-value description example
    key-value main example.4th

    key-list dependencies f git https://github.com/VitaSound/f tag 0.2.4
    key-list dependencies ttester git https://github.com/VitaSound/ttester tag 1.1.0
end-forth-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like that the file itself is also written in a Forth-like style. It is not JSON, YAML, or TOML. It is a tiny declarative Forth format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies from Git
&lt;/h2&gt;

&lt;p&gt;The main reason I started making &lt;code&gt;fmix&lt;/code&gt; was Git dependencies.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;package.4th&lt;/code&gt;, you can define a dependency like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key-list dependencies ftest git https://github.com/VitaSound/ftest.git branch main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or like this, if you need a specific version via a tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key-list dependencies ftest git https://github.com/VitaSound/ftest.git tag 0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you only need to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix packages.get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dependencies will be installed into the local project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./forth-packages/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure makes it possible to keep different versions of the same library side by side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;forth-packages/
  f/
    0.2.4/
  ttester/
    1.1.0/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can include a dependency with the usual &lt;code&gt;require&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require ./forth-packages/f/0.2.4/f.4th
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Support for theforth.net
&lt;/h2&gt;

&lt;p&gt;Although I wanted Git dependencies, I did not want to completely abandon the existing ecosystem.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;fmix&lt;/code&gt; can also work with packages from &lt;code&gt;theforth.net&lt;/code&gt; through &lt;code&gt;f.4th&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key-list dependencies f 0.2.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means one project can use both Git dependencies and packages from &lt;code&gt;theforth.net&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rcr8nz7uo6p8j4kevur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rcr8nz7uo6p8j4kevur.png" alt=" " width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;

&lt;p&gt;Another basic thing I was missing was running tests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fmix&lt;/code&gt; has this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs tests from the &lt;code&gt;tests/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;You can also run a specific file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix &lt;span class="nb"&gt;test &lt;/span&gt;tests/some_test.4th
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tests use &lt;code&gt;ttester&lt;/code&gt;. If &lt;code&gt;ttester&lt;/code&gt; exists in the project dependencies, &lt;code&gt;fmix&lt;/code&gt; loads it from &lt;code&gt;./forth-packages&lt;/code&gt;. If not, it uses the version from the installed &lt;code&gt;FMIX_HOME&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For me, this matters: tests should run with one command. Even if the project is small.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flo75871pjzbs5lre1cp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flo75871pjzbs5lre1cp3.png" alt=" " width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Was Fixed in Recent Versions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fmix&lt;/code&gt; has already gone through several generations of changes.&lt;/p&gt;

&lt;p&gt;I wrote the first versions myself. But I am not a very strong Forth developer, so many things were difficult for me. I made one major refactoring with the help of Gemini. I finished the latest version with Cursor.&lt;/p&gt;

&lt;p&gt;The latest releases fixed quite a few practical problems.&lt;/p&gt;

&lt;p&gt;GitHub Actions CI was added: the workflow builds &lt;a href="https://www.gnu.org/software/gforth/" rel="noopener noreferrer"&gt;GForth&lt;/a&gt; 0.7.9 from source and runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gforth fmix.4th &lt;span class="nt"&gt;-e&lt;/span&gt; version
gforth fmix.4th &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later, a release pipeline was added: when a version tag is pushed, a GitHub Release is created automatically, and the release text is taken from &lt;code&gt;.github/RELEASE_NOTES_X.Y.Z.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Path handling was also fixed. Previously, part of the logic depended on &lt;code&gt;PWD&lt;/code&gt;. Now the project is detected from the directory where the command is started. This matters because &lt;code&gt;FMIX_HOME&lt;/code&gt; points to the installed &lt;code&gt;fmix&lt;/code&gt;, while commands should operate on the current project.&lt;/p&gt;

&lt;p&gt;Several dependency installation problems were fixed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git clone&lt;/code&gt; now checks its result;&lt;/li&gt;
&lt;li&gt;failed shell commands make GForth exit with code &lt;code&gt;1&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;parent directories are created before &lt;code&gt;git clone&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git fetch&lt;/code&gt; and &lt;code&gt;checkout&lt;/code&gt; are executed so that failures are not hidden;&lt;/li&gt;
&lt;li&gt;Git commands use &lt;code&gt;GIT_TERMINAL_PROMPT=0&lt;/code&gt;, so the command does not hang in a non-interactive environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another important thing is input validation. &lt;code&gt;fmix&lt;/code&gt; validates package names, versions, paths, Git URLs, and Git refs before calling &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, or &lt;code&gt;git&lt;/code&gt;. It is not a complex escaping system, but a simple whitelist. It rejects spaces, &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, backticks, pipes, and other shell metacharacters.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Unexpected Terminal Problem
&lt;/h2&gt;

&lt;p&gt;There was a separate story with the terminal. Before using the &lt;code&gt;fmix&lt;/code&gt; command in the console, it had to be configured as an alias.&lt;/p&gt;

&lt;p&gt;When running through a simple alias like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;fmix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'gforth "$FMIX_HOME/fmix.4th" -e'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the terminal sometimes ended up in a bad state after commands finished. Strange symbols appeared in the prompt, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0c0c
[?2004l
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was especially visible in WSL.&lt;/p&gt;

&lt;p&gt;In version &lt;code&gt;0.4.4&lt;/code&gt;, I switched to the &lt;code&gt;bin/fmix&lt;/code&gt; launcher script. It restores the TTY after commands, resets bracketed paste mode, and briefly drains queued input from &lt;code&gt;/dev/tty&lt;/code&gt;. To be honest, I would definitely not have solved all of that on my own. I patiently helped the neural network try hypothesis after hypothesis to fix the problem. Unlike the AI tools I had tried before, this time I only had to stop the AI once and create a new context because it seemed to get stuck in an endless correction loop.&lt;/p&gt;

&lt;p&gt;But it worked, and now instead of an alias it is recommended to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FMIX_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/fmix"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FMIX_HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then run it simply as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Right now installation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/VitaSound/fmix.git ~/fmix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add this to your shell config, for example &lt;code&gt;.bashrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FMIX_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/fmix"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FMIX_HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, the command should work directly in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fmix version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important: at the moment &lt;code&gt;fmix&lt;/code&gt; expects Linux, Git, sed, cp, and &lt;a href="https://www.gnu.org/software/gforth/" rel="noopener noreferrer"&gt;GForth&lt;/a&gt; 0.7.9.&lt;/p&gt;

&lt;p&gt;I do not recommend using earlier versions of GForth or the &lt;a href="https://snapcraft.io/" rel="noopener noreferrer"&gt;Snap&lt;/a&gt; version. Snap runs programs in a confined environment, so the current directory and paths may not match what the shell session expects. This breaks commands like &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;packages.get&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is better to use GForth from apt, a local build, or a tarball from the &lt;a href="https://www.gnu.org/software/gforth/" rel="noopener noreferrer"&gt;official GForth page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;I think old languages really need tools like this to fit modern realities.&lt;/p&gt;

&lt;p&gt;They do not have to be huge. They do not have to be perfect. But they should at least cover the basic scenarios. When a language has convenient tooling around projects, it becomes friendlier.&lt;/p&gt;

&lt;p&gt;It is easier for a newcomer to try the language. It is easier for an experienced user to start a new project. Libraries are easier to reuse. Tests are easier to run.&lt;/p&gt;

&lt;p&gt;This does not change the language itself, but it changes the experience of working with it. It allows an old tool to be used in new projects.&lt;/p&gt;

&lt;p&gt;In the case of Forth, this is especially interesting. Forth is often seen as something old, strange, and niche. But maybe part of that “oldness” is not only in the language itself, but also in the absence of familiar modern tooling around it.&lt;/p&gt;

&lt;p&gt;If we add convenient tools, the language can feel very different.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsixaust3ka33eqv8z3zr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsixaust3ka33eqv8z3zr.png" alt=" " width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Next
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fmix&lt;/code&gt; is still small and experimental.&lt;/p&gt;

&lt;p&gt;Right now it can create packages, fetch dependencies from Git and &lt;code&gt;theforth.net&lt;/code&gt;, run tests, and publish releases through GitHub Actions.&lt;/p&gt;

&lt;p&gt;In the future, I would like to improve compatibility, add documentation, and maybe add more commands around Forth package development.&lt;/p&gt;

&lt;p&gt;But even now, it has become much more convenient for me to work on my Forth projects. But that is another story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/VitaSound/fmix" rel="noopener noreferrer"&gt;FMix on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/gforth/" rel="noopener noreferrer"&gt;GForth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://theforth.net/" rel="noopener noreferrer"&gt;theforth.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/TG9541/stm8ef" rel="noopener noreferrer"&gt;STM8 eForth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.me/forthchat" rel="noopener noreferrer"&gt;[NF] Forth-like languages, язык Форт&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.me/ruforth" rel="noopener noreferrer"&gt;[TF] Форт и общение фортеров&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>forth</category>
      <category>opensource</category>
      <category>tooling</category>
      <category>ai</category>
    </item>
    <item>
      <title>AD9833 DDS Generator Board</title>
      <dc:creator>Alexey Bolshakov</dc:creator>
      <pubDate>Thu, 19 Mar 2026 16:52:05 +0000</pubDate>
      <link>https://dev.to/ua3mqj/ad9833-dds-generator-board-10p7</link>
      <guid>https://dev.to/ua3mqj/ad9833-dds-generator-board-10p7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felfheihtro5s2nvhj93x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felfheihtro5s2nvhj93x.png" alt="Image AD9833"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A frequency generator board based on the AD9833 chip. Purchased as a frequency-tunable clock signal source. The board is equipped with a 25 MHz oscillator. It can generate signals from 0 to 12.5 MHz (though waveform quality may not be satisfactory across the entire range). The waveform can be switched between sine, sawtooth, and square wave. All necessary signals are brought out to pin headers. Controlled via SPI interface.&lt;/p&gt;

&lt;p&gt;Architecturally, it contains one 28-bit phase accumulator register, two frequency tuning registers, and two phase offset registers. Only one pair (frequency/phase) can be active at a time. This allows for frequency modulation without phase discontinuity or phase modulation, if needed. However, there is no dedicated hardware input for selecting the register pair – switching is possible only via SPI commands.&lt;/p&gt;

&lt;p&gt;Oscilloscope waveforms follow below.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test setup
&lt;/h2&gt;

&lt;p&gt;To control the DDS, commands must be sent via SPI. I am using an STM8 (STM8S105K4T6) with its hardware SPI. It was crucial to find the correct SPI operating mode. For the STM8 (and likely for STM32 as well), this is Mode 2 (CPOL = 1, CPHA = 0). For more details, refer to the documentation.(&lt;a href="https://www.st.com/resource/en/reference_manual/cd00190271-stm8s-series-and-stm8af-series-8bit-microcontrollers-stmicroelectronics.pdf" rel="noopener noreferrer"&gt;RM0016&lt;/a&gt; - STM8S Series and STM8AF Series 8-bit microcontrollers (20.4 SPI registers)). Oscilloscope: C1-112A, 10 MHz bandwidth; probe: generic Chinese P6100 type, rated for 100 MHz. Multimeter: Aneng M20, used as a frequency counter.&lt;/p&gt;

&lt;p&gt;I did not write firmware for the microcontroller in the conventional sense. Instead, I flashed the STM8 with the &lt;a href="https://github.com/TG9541/stm8ef/wiki/STM8-eForth-Programming" rel="noopener noreferrer"&gt;eForth&lt;/a&gt; system and connected to it via a serial console. From the console, I sent SPI commands directly, after pre-defining the necessary intermediate words. It looked roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

: word.hi $0100 / ;
: word.lo $00ff and ;

: spi_wr8 SPI_DR c! TXE? ;
: spi_wr16  dup word.lo swap word.hi spi_wr8 spi_wr8 ;
: spi_wr16n dup word.lo swap word.hi nss.lo spi_wr8 spi_wr8 BSY? nss.hi ;
: spi_wr32n nss.lo spi_wr16 spi_wr16 bsy? nss.hi ;

...

$2100 spi_wr16n         // reset
$5c20 $4a3d spi_wr32n   // freq0
%0111111111111111 %0100000000000000 spi_wr32n   // freq0
%0111111111111111 spi_wr16n %0100000000000000 spi_wr16n
$9c29 $8a3d spi_wr32n   // freq1
$c000 spi_wr16n         // phase0f
$e000 spi_wr16n         // phase1
$2000 spi_wr16n         // sine
$2020 spi_wr16n         // square
$2002 spi_wr16n         // saw      
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It turned out that I used the system interactively, rather than reflashing the firmware each time. This approach reminds me of working with the Erlang virtual machine, where you can debug and modify running code on the fly on production.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmenxcavudzcsq9l18i1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmenxcavudzcsq9l18i1.png" alt="Image stage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Oscilloscope Waveforms
&lt;/h2&gt;

&lt;p&gt;Sine 1 KHz&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgre9dzga7hhnrybkjebz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgre9dzga7hhnrybkjebz.png" alt="Image Sine1 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  10 KHz, 50 KHz, 100 KHz, 500 KHz, 2 MHz, 4 MHz, 8 MHz
  &lt;br&gt;
10 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dxg89qfbljtj3vhuzt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dxg89qfbljtj3vhuzt6.png" alt="Image 10 KHz"&gt;&lt;/a&gt;

&lt;p&gt;50 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi53qz4gy44afwajj3dt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi53qz4gy44afwajj3dt.png" alt="Image 50 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;100 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh5nv6oq9bxomzjp8gg2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh5nv6oq9bxomzjp8gg2.png" alt="Image 100 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;500 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ve7ncv6li2zsxvm1ov4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ve7ncv6li2zsxvm1ov4.png" alt="Image 500 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6gq0go0fuc27iexxcp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6gq0go0fuc27iexxcp8.png" alt="Image 2 MHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxk39bzz5ln6jzyvlcqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxk39bzz5ln6jzyvlcqb.png" alt="Image 4 MHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiy4salusx0vv0pp57t6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiy4salusx0vv0pp57t6.png" alt="Image 8 MHz"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Saw 1 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahqs1lpi4brm6jmor8dc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahqs1lpi4brm6jmor8dc.png" alt="Image Saw 1 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  10 KHz, 50 KHz, 100 KHz, 500 KHz, 2 MHz, 4 MHz, 8 MHz
  &lt;br&gt;
10 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc7dlcl09rcqd9u2qcuy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc7dlcl09rcqd9u2qcuy.png" alt="Image 10 KHz"&gt;&lt;/a&gt;

&lt;p&gt;50 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u9000y6t6jk509jp808.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u9000y6t6jk509jp808.png" alt="Image 50 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;100 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmjy5uomci3yxtp55ax0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmjy5uomci3yxtp55ax0.png" alt="Image 100 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;500 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgierffe0rmqpqxqed7lg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgierffe0rmqpqxqed7lg.png" alt="Image 500 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqynd0h8twct13rtitx2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqynd0h8twct13rtitx2.png" alt="Image 2 MHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdxyldw8i9zbh3bmn3mn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdxyldw8i9zbh3bmn3mn.png" alt="Image 4 MHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F045pv58x0580w4wo25ao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F045pv58x0580w4wo25ao.png" alt="Image 8 MHz"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;At 2 MHz, floating peaks on the sawtooth waveform are already visible. At 4 MHz, they become very pronounced. And at 8 MHz, we end up with 25/8 = 3.125 samples per period.&lt;/p&gt;

&lt;p&gt;Purely to explore my oscilloscope and probes, I also tried the square wave mode. However, I couldn't be bothered to recalculate the frequency grid. In square wave mode, the period is twice as long, so the resulting frequencies were different.&lt;/p&gt;

&lt;p&gt;5 kHz&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhoeizj2fsnbn97c61vcg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhoeizj2fsnbn97c61vcg.jpeg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  25 KHz, 250 KHz, 1 MHz, 2 MHz, 4 MHz
  &lt;br&gt;
25 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq3p19qbz6of1ut4cio4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq3p19qbz6of1ut4cio4.png" alt="Image 25 KHz"&gt;&lt;/a&gt;

&lt;p&gt;250 KHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxf9hzwuaib7166jmyri8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxf9hzwuaib7166jmyri8.png" alt="Image 250 KHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz28r0lvfv3po9dkv0bd9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz28r0lvfv3po9dkv0bd9.png" alt="Image 1 MHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3ieelatpjf4uuey8o2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3ieelatpjf4uuey8o2v.png" alt="Image 2 MHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 MHz&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q96bjuitxchsjbrhf0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q96bjuitxchsjbrhf0p.png" alt="Image 4 MHz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 MHz more wide&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F809p1xc65h4nbwnxhisf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F809p1xc65h4nbwnxhisf.png" alt="Image 4 MHz wide"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Starting from 1 MHz, jitter is obviously present; at 2 MHz, it becomes visible on the oscilloscope waveform.&lt;/p&gt;

&lt;p&gt;By the way (for those who aren't aware), it's important to note that at high frequencies, to avoid degrading signal edges, you should use the probe in 10:1 attenuation mode. This reduces the probe's loading effect on the circuit (and it's even better to avoid using alligator clips altogether). However, this will require increasing the oscilloscope's input amplifier gain by a factor of 10 to compensate for the attenuation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm701spnv3sctzfocffhw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm701spnv3sctzfocffhw.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, don't forget to adjust the probe compensation for accurate waveform display, using a small dielectric (non-metallic) screwdriver.&lt;/p&gt;

&lt;p&gt;For comparison, here's a photo showing the same signal — a 2 MHz square wave — captured with the probe set to 1X versus 10X attenuation.&lt;/p&gt;

&lt;p&gt;2 MHz - 1х&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farvg1btsyj3ank7liduy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farvg1btsyj3ank7liduy.png" alt="Image 2 MHz - 1х"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 MHz - 10х&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvems2wak8undlhvq8au.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvems2wak8undlhvq8au.png" alt="Image 2 MHz - 10х"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the way, the original probe that came with the C1-112A is also quite decent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98989yjzxeu24nflfpx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98989yjzxeu24nflfpx1.png" alt="Image probe С1-112А"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 MHz - 10х&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5c12agelm0uzcx9xv3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5c12agelm0uzcx9xv3h.png" alt="Image 2 MHz - 10х"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I acquired this board for experiments in the field of hybrid sound synthesis for my synthesizer project. In the future, I plan to use it for a DCO (Digitally Controlled Oscillator) module — but not with a fixed Master Clock frequency; rather, with a frequency that is a multiple of the generated note frequency. This way, the DAC in the DCO can utilize its full scale and DAC resolution without skipping steps. All of this is part of the effort to combat generation accuracy loss and aliasing... But that's a whole different story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;1 The original version of this article&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mysku.club/blog/aliexpress/79168.html" rel="noopener noreferrer"&gt;https://mysku.club/blog/aliexpress/79168.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 My Telegram channel "Programmer's Tips": &lt;a href="https://t.me/ProProgs" rel="noopener noreferrer"&gt;https://t.me/ProProgs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3 eForth - Forth for microcontrollers&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TG9541/stm8ef/wiki/STM8-eForth-Programming" rel="noopener noreferrer"&gt;https://github.com/TG9541/stm8ef/wiki/STM8-eForth-Programming&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 Links about my synth project&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/UA3MQJ/fpga-synth/wiki" rel="noopener noreferrer"&gt;https://github.com/UA3MQJ/fpga-synth/wiki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/VitaSound/hdl-modules" rel="noopener noreferrer"&gt;https://github.com/VitaSound/hdl-modules&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://soundcloud.com/vitasynth" rel="noopener noreferrer"&gt;https://soundcloud.com/vitasynth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.youtube.com/channel/UCGi-A9KGhKhKzVT_baVw3HA" rel="noopener noreferrer"&gt;http://www.youtube.com/channel/UCGi-A9KGhKhKzVT_baVw3HA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vk.com/vitasound" rel="noopener noreferrer"&gt;https://vk.com/vitasound&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://boosty.to/vitasound" rel="noopener noreferrer"&gt;https://boosty.to/vitasound&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dds</category>
      <category>microchips</category>
      <category>stm8</category>
      <category>microcontroller</category>
    </item>
    <item>
      <title>Плата DDS генератора AD9833</title>
      <dc:creator>Alexey Bolshakov</dc:creator>
      <pubDate>Thu, 19 Mar 2026 16:35:39 +0000</pubDate>
      <link>https://dev.to/ua3mqj/plata-dds-ghienieratora-ad9833-bh7</link>
      <guid>https://dev.to/ua3mqj/plata-dds-ghienieratora-ad9833-bh7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felfheihtro5s2nvhj93x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felfheihtro5s2nvhj93x.png" alt="Image AD9833"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Плата с DDS генератором частоты на базе микросхемы AD9833. Приобретена в качестве источника тактового сигнала, перестраиваемого по частоте. На плате установлен генератор на 25 МГц. Может генерировать сигналы от 0 до 12.5 МГц (но не во всем диапазоне вас устроит форма сигнала). Можно переключать форму сигнала: синус, пила, меандр. Всё, что нужно, выведено на штыревые контакты. Управляется по SPI. Архитектурно содержит один 28-битный регистр аккумулятора, два регистра приращения, два регистра фазы. В один момент времени может быть выбрана только одна из двух пар. Это позволит, если необходимо, сформировать частотную модуляцию сигнала без разрыва фазы или фазовую модуляцию. Однако, отдельного входа выбора пары нет, переключение возможно только командами по SPI.&lt;/p&gt;

&lt;p&gt;Далее осциллограммы&lt;/p&gt;




&lt;h2&gt;
  
  
  Стенд
&lt;/h2&gt;

&lt;p&gt;Для управления DDS нужно подавать команды по SPI. Использую STM8 (STM8S105K4T6) и его аппаратный SPI. Очень важно было найти правильный режим работы SPI. Для STM8 (и наверняка для STM32) это будет mode 2 (CPOL = 1 and CPHA = 0). Подробнее, см. документацию(&lt;a href="https://www.st.com/resource/en/reference_manual/cd00190271-stm8s-series-and-stm8af-series-8bit-microcontrollers-stmicroelectronics.pdf" rel="noopener noreferrer"&gt;RM0016&lt;/a&gt; - STM8S Series and STM8AF Series 8-bit microcontrollers (20.4 SPI registers)). Осциллограф С1-112А полоса 10 МГц, щуп китайский на 100 китайских МГц (P6100). Мультиметр Aneng M20 в качестве частотомера.&lt;/p&gt;

&lt;p&gt;Прошивку для микроконтроллера в привычном для всех виде я не писал. Просто поставил на микроконтроллер STM8 систему &lt;a href="https://github.com/TG9541/stm8ef/wiki/STM8-eForth-Programming" rel="noopener noreferrer"&gt;eForth&lt;/a&gt; и далее подключался к нему через консоль. И уже в консоли отправлял команды через SPI, задав перед этим необходимые промежуточные слова. Выглядело это примерно вот так&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

: word.hi $0100 / ;
: word.lo $00ff and ;

: spi_wr8 SPI_DR c! TXE? ;
: spi_wr16  dup word.lo swap word.hi spi_wr8 spi_wr8 ;
: spi_wr16n dup word.lo swap word.hi nss.lo spi_wr8 spi_wr8 BSY? nss.hi ;
: spi_wr32n nss.lo spi_wr16 spi_wr16 bsy? nss.hi ;

...

$2100 spi_wr16n         // reset
$5c20 $4a3d spi_wr32n   // freq0
%0111111111111111 %0100000000000000 spi_wr32n   // freq0
%0111111111111111 spi_wr16n %0100000000000000 spi_wr16n
$9c29 $8a3d spi_wr32n   // freq1
$c000 spi_wr16n         // phase0f
$e000 spi_wr16n         // phase1
$2000 spi_wr16n         // sine
$2020 spi_wr16n         // square
$2002 spi_wr16n         // saw      
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Получилось, что я пользовался системой интерактивно, а не перепрошивая каждый раз. Мне такой подход напоминает работу с Erlang виртуальной машиной, когда можно на ходу отлаживать и менять работающий код.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmenxcavudzcsq9l18i1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmenxcavudzcsq9l18i1.png" alt="Image stage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Осциллограммы
&lt;/h2&gt;

&lt;p&gt;Синус 1 КГц&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgre9dzga7hhnrybkjebz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgre9dzga7hhnrybkjebz.png" alt="Image Синус 1 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  10 КГц, 50 КГц, 100 КГц, 500 КГц, 2 МГц, 4 МГц, 8 МГц
  &lt;br&gt;
10 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dxg89qfbljtj3vhuzt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dxg89qfbljtj3vhuzt6.png" alt="Image 10 КГц"&gt;&lt;/a&gt;

&lt;p&gt;50 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi53qz4gy44afwajj3dt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi53qz4gy44afwajj3dt.png" alt="Image 50 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;100 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh5nv6oq9bxomzjp8gg2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh5nv6oq9bxomzjp8gg2.png" alt="Image 100 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;500 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ve7ncv6li2zsxvm1ov4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ve7ncv6li2zsxvm1ov4.png" alt="Image 500 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6gq0go0fuc27iexxcp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6gq0go0fuc27iexxcp8.png" alt="Image 2 МГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxk39bzz5ln6jzyvlcqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxk39bzz5ln6jzyvlcqb.png" alt="Image 4 МГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiy4salusx0vv0pp57t6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiy4salusx0vv0pp57t6.png" alt="Image 8 МГц"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Пила 1 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahqs1lpi4brm6jmor8dc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahqs1lpi4brm6jmor8dc.png" alt="Image Пила 1 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  10 КГц, 50 КГц, 100 КГц, 500 КГц, 2 МГц, 4 МГц, 8 МГц
  &lt;br&gt;
10 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc7dlcl09rcqd9u2qcuy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc7dlcl09rcqd9u2qcuy.png" alt="Image 10 КГц"&gt;&lt;/a&gt;

&lt;p&gt;50 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u9000y6t6jk509jp808.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u9000y6t6jk509jp808.png" alt="Image 50 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;100 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmjy5uomci3yxtp55ax0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmjy5uomci3yxtp55ax0.png" alt="Image 100 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;500 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgierffe0rmqpqxqed7lg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgierffe0rmqpqxqed7lg.png" alt="Image 500 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqynd0h8twct13rtitx2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqynd0h8twct13rtitx2.png" alt="Image 2 МГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdxyldw8i9zbh3bmn3mn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdxyldw8i9zbh3bmn3mn.png" alt="Image 4 МГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F045pv58x0580w4wo25ao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F045pv58x0580w4wo25ao.png" alt="Image 8 МГц"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;На 2 МГц уже видно плавающие пики пилы. На 4 МГц видно уже очень отчетливо. Ну и на 8 МГц у нас получается 25/8 = 3,125 отсчета на период.&lt;/p&gt;

&lt;p&gt;Исключительно ради изучения своего осциллографа и щупов, попробовал меандр. Правда сетку частот пересчитывать было лень. В режиме меандра, период в 2 раза длиннее, поэтому частоты получились другие.&lt;/p&gt;

&lt;p&gt;5 КГц&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fge7bqykm1edhv6737e3z.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fge7bqykm1edhv6737e3z.jpeg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  25 КГц, 250 КГц, 1 МГц, 2 МГц, 4 МГц
  &lt;br&gt;
25 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq3p19qbz6of1ut4cio4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq3p19qbz6of1ut4cio4.png" alt="Image 25 КГц"&gt;&lt;/a&gt;

&lt;p&gt;250 КГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxf9hzwuaib7166jmyri8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxf9hzwuaib7166jmyri8.png" alt="Image 250 КГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz28r0lvfv3po9dkv0bd9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz28r0lvfv3po9dkv0bd9.png" alt="Image 1 МГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3ieelatpjf4uuey8o2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3ieelatpjf4uuey8o2v.png" alt="Image 2 МГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 МГц&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q96bjuitxchsjbrhf0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q96bjuitxchsjbrhf0p.png" alt="Image 4 МГц"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 МГц более развернуто&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F809p1xc65h4nbwnxhisf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F809p1xc65h4nbwnxhisf.png" alt="Image 4 МГц wide"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Начиная с 1 МГц, ясное дело - джиттер, на 2 МГц его видно на осциллограмме.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Кстати (для тех, кто не в курсе), важно, что на высоких частотах, чтобы не портить фронты, нужно использовать щуп в режиме 1:10, чтобы он меньше влиял на схему (а еще лучше не использовать крокодил). Но потребуется усилить сигнал в 10 раз уже на входном усилителе осциллографа.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm701spnv3sctzfocffhw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm701spnv3sctzfocffhw.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Так же, не стоит забывать подстраивать делитель для правильного отображения, диэлектрической отверточкой.&lt;/p&gt;

&lt;p&gt;Для сравнения, фото, как выглядит один и тот же сигнал - меандр 2 МГц, но 1Х или 10Х&lt;/p&gt;

&lt;p&gt;2 МГц - 1х&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farvg1btsyj3ank7liduy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farvg1btsyj3ank7liduy.png" alt="Image 2 МГц - 1х"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 МГц - 10х&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvems2wak8undlhvq8au.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvems2wak8undlhvq8au.png" alt="Image 2 МГц - 10х"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Кстати, родной щуп от С1-112А тоже неплох&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98989yjzxeu24nflfpx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98989yjzxeu24nflfpx1.png" alt="Image щуп от С1-112А"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 МГц - 10х&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5c12agelm0uzcx9xv3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5c12agelm0uzcx9xv3h.png" alt="Image 2 МГц - 10х"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Выводы
&lt;/h2&gt;

&lt;p&gt;Эту плату я брал для экспериментов в области создания гибридного синтеза звука своего синтезаторного проекта. В дальнейшем планируется использовать для модуля генератора DCO (Digital VCO) но не с фиксированной частотой Master Clock а с частотой, кратной частоте генерации, для того, чтобы DAC в DCO использовал всю свою шкалу и разрядность, не пропускал шаги. Все это в рамках борьбы с потерей точности генерации, борьбы с алиасингом... Но это уже совсем другая история&lt;/p&gt;

&lt;h2&gt;
  
  
  Ссылки
&lt;/h2&gt;

&lt;p&gt;1 Исходная версия этой статьи&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mysku.club/blog/aliexpress/79168.html" rel="noopener noreferrer"&gt;https://mysku.club/blog/aliexpress/79168.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 Мой канал в TG "Советы программиста" &lt;a href="https://t.me/ProProgs" rel="noopener noreferrer"&gt;https://t.me/ProProgs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3 eForth - язык Forth для микроконтроллеров &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TG9541/stm8ef/wiki/STM8-eForth-Programming" rel="noopener noreferrer"&gt;https://github.com/TG9541/stm8ef/wiki/STM8-eForth-Programming&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 Ссылки по моему синтезаторному проекту&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/UA3MQJ/fpga-synth/wiki" rel="noopener noreferrer"&gt;https://github.com/UA3MQJ/fpga-synth/wiki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/VitaSound/hdl-modules" rel="noopener noreferrer"&gt;https://github.com/VitaSound/hdl-modules&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://soundcloud.com/vitasynth" rel="noopener noreferrer"&gt;https://soundcloud.com/vitasynth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.youtube.com/channel/UCGi-A9KGhKhKzVT_baVw3HA" rel="noopener noreferrer"&gt;http://www.youtube.com/channel/UCGi-A9KGhKhKzVT_baVw3HA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vk.com/vitasound" rel="noopener noreferrer"&gt;https://vk.com/vitasound&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://boosty.to/vitasound" rel="noopener noreferrer"&gt;https://boosty.to/vitasound&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dds</category>
      <category>microchips</category>
      <category>stm8</category>
      <category>microcontroller</category>
    </item>
  </channel>
</rss>
